home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 376-400 / disk_398 / dclock / dclock-handler.c < prev    next >
C/C++ Source or Header  |  1992-05-06  |  61KB  |  3,147 lines

  1. /* DClock-Handler.c *********************************************************
  2.  *
  3.  *    DClock-Handler.c ------    Dumb clock main handler routines,
  4.  *                display clock data, handle
  5.  *                DisplayBeep, etc.
  6.  *
  7.  *    Author ----------------    Olaf 'Olsen' Barthel, MXM
  8.  *                Brabeckstrasse 35
  9.  *                D-3000 Hannover 71
  10.  *
  11.  *                Federal Republic of Germany
  12.  *
  13.  *    This program truly is in the PUBLIC DOMAIN. Written on a cold
  14.  *    and damp September evening, hoping the next morning would  be
  15.  *    better.
  16.  *
  17.  *    Compiled using Aztec C 5.0b, CygnusEd Professional 2 & ARexx.
  18.  *
  19.  ***************************************************************************/
  20.  
  21.     /* Signal flag aliases. */
  22.  
  23. #define SIG_CLICK    SIGBREAKF_CTRL_C
  24. #define SIG_CLOSE    SIGBREAKF_CTRL_D
  25. #define SIG_TIMER    SIGBREAKF_CTRL_E
  26. #define SIG_TOGGL    SIGBREAKF_CTRL_F
  27. #define SIG_SHAKE    SIGBREAKF_CTRL_E
  28. #define SIG_BENCH    (1 << BenchSig)
  29. #define SIG_WINDO    (1 << Window -> UserPort -> mp_SigBit)
  30. #define SIG_DISPL    (1 << DisplaySig)
  31. #define SIG_SPEECH    (1 << SpeechSig)
  32.  
  33.     /* Prototypes. */
  34.  
  35. struct InputEvent *    EventHandler(struct InputEvent *Event);
  36. VOID             Click(VOID);
  37. VOID            FlushSound(VOID);
  38. UBYTE            InitSound(VOID);
  39. UBYTE            InitHandler(VOID);
  40. VOID            FlushHandler(VOID);
  41. VOID            ModifiedCloseWBench(struct Screen *);
  42. VOID            AudioBeep(BYTE Volume);
  43. VOID            VideoBeep(struct Screen *Screen,BYTE Perform);
  44. VOID            ModifiedDisplayBeep(struct Screen *Screen);
  45. VOID            PrintIt(STRPTR TimeBuff);
  46. VOID            Ring(LONG Tea);
  47. VOID            ShowTime(UBYTE ReallyDoIt,UBYTE Force);
  48. ULONG            MaxMemSize(ULONG MemType);
  49. struct Screen *        FindTheBench(VOID);
  50. VOID            ShutDown(LONG HandShake);
  51. LONG            RangeRand(LONG);
  52. struct Process *    CreateFuncProc(char *Name,LONG Priority,APTR InitCode,ULONG StackSize);
  53. UBYTE            MaxFontWidth(VOID);
  54. VOID            DeleteDummyRPort(VOID);
  55. BYTE            CreateDummyRPort(VOID);
  56. LONG            PlayChime(VOID);
  57. VOID            StopChime(VOID);
  58. LONG            FindChunk(ULONG ChunkName,BPTR FileHandle);
  59. LONG            LoadChimeSound(char *Name);
  60.  
  61. extern VOID        NewDisplayBeep(VOID);
  62. extern VOID        NewCloseWBench(VOID);
  63.  
  64.     /* The ARexx server routines. */
  65.  
  66. STRPTR            CheckDClockStatus(STRPTR);
  67. VOID            RexxServer(VOID);
  68.  
  69.     /* Interrupt register saving functions. */
  70.  
  71. VOID            int_start(VOID);
  72. VOID            int_end(VOID);
  73.  
  74.     /* System specific function. */
  75.  
  76. LONG            _main(VOID);
  77.  
  78.     /* The speech server. */
  79.  
  80. VOID            SpeechServer(VOID);
  81.  
  82.     /* The magic pragmas. */
  83.  
  84. #pragma regcall(EventHandler(a0))
  85. #pragma regcall(ModifiedDisplayBeep(a0))
  86.  
  87.     /* Some global data. */
  88.  
  89. struct ArpBase        *ArpBase;
  90. struct IntuitionBase    *IntuitionBase;
  91. struct GfxBase        *GfxBase;
  92. struct Library        *DiskfontBase;
  93. struct RexxHostBase    *RexxHostBase;
  94. struct Window        *Window;
  95. struct Process        *HandlerProcess;
  96. struct Process        *RexxProcess;
  97. struct MsgPort        *RexxTaskPort;
  98. struct Process        *SpeechProcess;
  99. struct DSeg        *DSeg;
  100. BYTE             NewKick = FALSE;
  101. BYTE             Printed = FALSE;
  102. LONG             BenchSig = -1;
  103. LONG             DisplaySig = -1;
  104. LONG             SpeechSig = -1;
  105.  
  106.     /* Our current version tag. */
  107.  
  108. const char *VersionTag = "$VER: DClock-Handler 1.29 (15 Sep 1990)\n\r";
  109.  
  110.     /* Online time. */
  111.  
  112. UBYTE OnlineHours = 0,OnlineMinutes = 0,OnlineSeconds = 0;
  113.  
  114.     /* The chime data. */
  115.  
  116. struct IOAudio        *ChimeAudioBlock;
  117. struct MsgPort        *ChimeReplyPort;
  118. UBYTE            *ChimeWaveMap;
  119.  
  120.     /* Our dummy RastPort. */
  121.  
  122. struct BitMap        *DummyMap;
  123. struct RastPort        *DummyRPort;
  124. struct TextFont        *DummyFont;
  125. BYTE             DummyDepth;
  126.  
  127.     /* Static DClock window size and location. */
  128.  
  129. LONG LeftEdge = 409,TopEdge = 1,Width = 176,Height = 8;
  130.  
  131.     /* Audio.device control structures. */
  132.  
  133. struct MsgPort        *AudioPort;
  134. struct IOAudio        *Audio;
  135.  
  136.     /* Input.device control structures. */
  137.  
  138. struct MsgPort        *InputDevPort;
  139. struct IOStdReq        *InputRequestBlock;
  140.  
  141.     /* The interrupt handler control. */
  142.  
  143. struct Interrupt    *InputHandler;
  144.  
  145.     /* Console.device control structures. */
  146.  
  147. struct Device        *ConsoleDevice;
  148. struct IOStdReq        *ConStdReq;
  149. struct InputEvent    *CopyEvent;
  150.  
  151.     /* The library offset pointers (old ones). */
  152.  
  153. VOID            *OldDisplayBeep,*OldCloseWBench;
  154.  
  155.     /* External data. */
  156.  
  157. extern ULONG        SoundData[67];
  158. extern ULONG        RingData[1831];
  159. extern USHORT        ClockMap[456];
  160. extern struct TextAttr    DefaultFont;
  161. extern struct IntuiText    TimeString;
  162. extern struct NewWindow    NewWindow;
  163. extern UBYTE        AnyChannel[4];
  164. extern UBYTE        SineWave[8];
  165. extern struct Image    ClockImage;
  166. extern struct Gadget    ClockGadget[2];
  167. extern struct NewWindow    NewClockWindow;
  168. extern struct IntuiText    ClockTxt[4];
  169.  
  170.     /* EventHandler(Event) :
  171.      *
  172.      *    This is the main interface to the handler
  173.      *    routine.
  174.      */
  175.  
  176. struct InputEvent *
  177. EventHandler(struct InputEvent *Event)
  178. {
  179.     struct InputEvent *ChainEvent;
  180.  
  181.     int_start();
  182.  
  183.     for(ChainEvent = Event ; ChainEvent ; ChainEvent = ChainEvent -> ie_NextEvent)
  184.     {
  185.             /* User wants to toggle text and memory mode. */
  186.  
  187.         if(ChainEvent -> ie_Class == IECLASS_RAWKEY && ChainEvent -> ie_Code == 0x5F && ChainEvent -> ie_Qualifier == RIGHT_AMIGA)
  188.         {
  189.             ChainEvent -> ie_Class = IECLASS_NULL;
  190.  
  191.             if(DSeg -> Child)
  192.                 Signal(DSeg -> Child,SIG_TOGGL);
  193.         }
  194.  
  195.             /* User wants to know the time? */
  196.  
  197.         if(ChainEvent -> ie_Class == IECLASS_RAWKEY && ChainEvent -> ie_Code == 0x5F && ChainEvent -> ie_Qualifier == RIGHT_ALT)
  198.         {
  199.             ChainEvent -> ie_Class = IECLASS_NULL;
  200.  
  201.             if(DSeg -> Child)
  202.                 Signal(DSeg -> Child,SIG_SPEECH);
  203.         }
  204.  
  205.             /* Modify the time display size? */
  206.  
  207.         if(ChainEvent -> ie_Class == IECLASS_RAWKEY && ChainEvent -> ie_Code == 0x46 && ChainEvent -> ie_Qualifier == RIGHT_AMIGA)
  208.         {
  209.             ChainEvent -> ie_Class = IECLASS_NULL;
  210.  
  211.             if(DSeg -> Child)
  212.                 Signal(DSeg -> Child,SIG_DISPL);
  213.         }
  214.  
  215.             /* If we can use it, initialize the copyevent and signal
  216.              * the main process to produce a click.
  217.              */
  218.  
  219.         if(ChainEvent -> ie_Class == IECLASS_RAWKEY && !(ChainEvent -> ie_Code & IECODE_UP_PREFIX) && DSeg -> Click)
  220.         {
  221.             CopyEvent -> ie_Class        = ChainEvent -> ie_Class;
  222.             CopyEvent -> ie_Code        = ChainEvent -> ie_Code;
  223.             CopyEvent -> ie_Qualifier    = ChainEvent -> ie_Qualifier;
  224.  
  225.                 /* This is safe from interrupt code, or
  226.                  * at least should be.
  227.                  */
  228.  
  229.             if(DSeg -> Child)
  230.                 Signal(DSeg -> Child,SIG_CLICK);
  231.         }
  232.     }
  233.  
  234.         /* I had a lot of trouble getting DClock-Handler to
  235.          * work with timer.device. Well, the only thing
  236.          * happening in time were system crashes, so I
  237.          * decided to use the timer entries of the
  238.          * InputEvent structures.
  239.          */
  240.  
  241.     if(Event -> ie_TimeStamp . tv_secs != DSeg -> LastSecs)
  242.     {
  243.         DSeg -> LastSecs = Event -> ie_TimeStamp . tv_secs;
  244.  
  245.         if(DSeg -> Child)
  246.             Signal(DSeg -> Child,SIG_TIMER);
  247.  
  248.         /* Each second we take a look at CIA B, port A
  249.          * to find out if a carrier signal is currently
  250.          * present at the serial port. Note that the port
  251.          * bits are low-active.
  252.          */
  253.  
  254.         if(!(ciab . ciapra & CIAF_COMCD))
  255.         {
  256.                 /* Are we online? */
  257.  
  258.             if(!DSeg -> Online)
  259.             {
  260.                 /* Reset counters. */
  261.  
  262.                 OnlineHours = OnlineMinutes = OnlineSeconds = 0;
  263.                 DSeg -> Online = TRUE;
  264.             }
  265.  
  266.                 /* Increment time counter. */
  267.  
  268.             if(++OnlineSeconds == 60)
  269.             {
  270.                 OnlineSeconds = 0;
  271.  
  272.                 if(++OnlineMinutes == 60)
  273.                 {
  274.                     OnlineMinutes = 0;
  275.  
  276.                     ++OnlineHours;
  277.                 }
  278.             }
  279.         }
  280.         else
  281.             DSeg -> Online = FALSE;
  282.     }
  283.  
  284.     int_end();
  285.  
  286.     return(Event);
  287. }
  288.  
  289.     /* Click() :
  290.      *
  291.      *    Produces the click (or what did you expect?).
  292.      */
  293.  
  294. VOID
  295. Click()
  296. {
  297.     char PrimaryBuffer[11];    /* Rawkey conversion buffer. */
  298.  
  299.     PrimaryBuffer[0] = 0;
  300.  
  301.         /* Convert the input event according to the
  302.          * current keymap settings.
  303.          */
  304.  
  305.     RawKeyConvert(CopyEvent,PrimaryBuffer,10,NULL);
  306.  
  307.         /* If it didn't produce a sensible result,
  308.          * don't click.
  309.          */
  310.  
  311.     if(!PrimaryBuffer[0] || PrimaryBuffer[0] == 0x9B)
  312.         return;
  313.  
  314.     Audio -> ioa_Volume = DSeg -> ClickVolume;
  315.  
  316.         /* Let it click. */
  317.  
  318. Tick:    if(CheckIO(Audio))
  319.         BeginIO(Audio);
  320. }
  321.  
  322.     /* FlushSound() :
  323.      *
  324.      *    Send the sound control data to NIL:
  325.      */
  326.  
  327. VOID
  328. FlushSound()
  329. {
  330.     if(Audio)
  331.     {
  332.             /* Audio device still open? */
  333.  
  334.         if(Audio -> ioa_Request . io_Device)
  335.         {
  336.             if(!CheckIO(Audio))
  337.                 WaitIO(Audio);
  338.  
  339.                 /* Free the channel(s). */
  340.  
  341.             CloseDevice(Audio);
  342.         }
  343.     }
  344.  
  345.         /* Delete the replyport. */
  346.  
  347.     if(AudioPort)
  348.         DeletePort(AudioPort);
  349. }
  350.  
  351.     /* InitSound() :
  352.      *
  353.      *    Sets up the audio control structures.
  354.      */
  355.  
  356. UBYTE
  357. InitSound()
  358. {
  359.     if(Audio = (struct IOAudio *)ArpAlloc(sizeof(struct IOAudio)))
  360.     {
  361.         if(AudioPort = (struct MsgPort *)CreatePort(NULL,0))
  362.         {
  363.                 /* Open the channel. */
  364.  
  365.             if(!OpenDevice(AUDIONAME,0,Audio,0))
  366.             {
  367.                     /* Try to allocate a vacant channel. */
  368.  
  369.                 Audio -> ioa_Request . io_Command            = ADCMD_ALLOCATE;
  370.                 Audio -> ioa_Request . io_Flags                = ADIOF_NOWAIT;
  371.                 Audio -> ioa_Request . io_Message . mn_Node . ln_Pri    = 100;
  372.                 Audio -> ioa_Request . io_Message . mn_ReplyPort    = AudioPort;
  373.                 Audio -> ioa_Data                    = AnyChannel;
  374.                 Audio -> ioa_Length                    = 4;
  375.  
  376.                     /* Try the allocation. */
  377.  
  378.                 BeginIO(Audio);
  379.  
  380.                     /* Did it return an error? */
  381.  
  382.                 if(!WaitIO(Audio))
  383.                 {
  384.                         /* Prepare it for the click. */
  385.  
  386.                     Audio -> ioa_Request . io_Command    = CMD_WRITE;
  387.                     Audio -> ioa_Request . io_Flags        = ADIOF_PERVOL | ADIOF_NOWAIT;
  388.                     Audio -> ioa_Period            = 180;
  389.                     Audio -> ioa_Volume            = 0;
  390.                     Audio -> ioa_Length            = 270;
  391.                     Audio -> ioa_Data            = (UBYTE *)SoundData;
  392.                     Audio -> ioa_Cycles            = 1;
  393.  
  394.                         /* Click once. */
  395.  
  396.                     BeginIO(Audio);
  397.                     WaitIO(Audio);
  398.  
  399.                     return(TRUE);
  400.                 }
  401.             }
  402.         }
  403.     }
  404.  
  405.     return(FALSE);
  406. }
  407.  
  408.     /* InitHandler() :
  409.      *
  410.      *    Open the console.device for keymap translation
  411.      *    and add the input.device handler.
  412.      */
  413.  
  414. UBYTE
  415. InitHandler()
  416. {
  417.     if(ConStdReq = (struct IOStdReq *)ArpAlloc(sizeof(struct IOStdReq)))
  418.     {
  419.         if(!OpenDevice("console.device",CONU_LIBRARY,ConStdReq,0))
  420.         {
  421.             if(InputDevPort = (struct MsgPort *)CreatePort(NULL,0))
  422.             {
  423.                 if(InputRequestBlock = (struct IOStdReq *)CreateStdIO(InputDevPort))
  424.                 {
  425.                     if(!OpenDevice("input.device",0,InputRequestBlock,0))
  426.                     {
  427.                         if(InputHandler = (struct Interrupt *)ArpAlloc(sizeof(struct Interrupt)))
  428.                         {
  429.                             if(CopyEvent = (struct InputEvent *)ArpAlloc(sizeof(struct InputEvent)))
  430.                             {
  431.                                 InputHandler -> is_Node . ln_Name    = "DClock-Handler";
  432.                                 InputHandler -> is_Node . ln_Pri    = 51;
  433.                                 InputHandler -> is_Code            = (VOID *)EventHandler;
  434.  
  435.                                 InputRequestBlock -> io_Command        = IND_ADDHANDLER;
  436.                                 InputRequestBlock -> io_Data        = (APTR)InputHandler;
  437.  
  438.                                 DoIO(InputRequestBlock);
  439.  
  440.                                 ConsoleDevice = ConStdReq -> io_Device;
  441.  
  442.                                 return(TRUE);
  443.                             }
  444.                         }
  445.                     }
  446.                 }
  447.             }
  448.         }
  449.     }
  450.  
  451.     return(FALSE);
  452. }
  453.  
  454.     /* FlushHandler() :
  455.      *
  456.      *    Closes the console.device and removes the
  457.      *    input.device handler from the chain.
  458.      */
  459.  
  460. VOID
  461. FlushHandler()
  462. {
  463.     if(ConsoleDevice)
  464.         CloseDevice(ConStdReq);
  465.  
  466.     if(InputRequestBlock)
  467.     {
  468.         if(InputRequestBlock -> io_Device)
  469.         {
  470.             InputRequestBlock -> io_Command    = IND_REMHANDLER;
  471.             InputRequestBlock -> io_Data    = (APTR)InputHandler;
  472.  
  473.             DoIO(InputRequestBlock);
  474.  
  475.             CloseDevice(InputRequestBlock);
  476.         }
  477.  
  478.         DeleteStdIO(InputRequestBlock);
  479.     }
  480.  
  481.     if(InputDevPort)
  482.         DeletePort(InputDevPort);
  483. }
  484.  
  485.     /* ModifiedCloseWBench():
  486.      *
  487.      *    Tells DClock to close its window before the
  488.      *    Workbench screen gets closed.
  489.      */
  490.  
  491. VOID
  492. ModifiedCloseWBench()
  493. {
  494.     if(Window)
  495.     {
  496.         struct Task    *ThisTask = SysBase -> ThisTask;
  497.         BYTE         CurrentPri;
  498.  
  499.             /* Shut down... */
  500.  
  501.         Signal(DSeg -> Child,SIG_BENCH);
  502.  
  503.             /* Careful - rather rude window close check,
  504.              * don't waste too much time in the loop.
  505.              */
  506.  
  507.         CurrentPri = SetTaskPri(ThisTask,0);
  508.  
  509.         while(Window);
  510.  
  511.         SetTaskPri(ThisTask,CurrentPri);
  512.     }
  513. }
  514.  
  515. VOID
  516. AudioBeep(BYTE Volume)
  517. {
  518.     struct IOAudio    *AudioBlock;
  519.     struct MsgPort    *ReplyPort;
  520.  
  521.         /* Allocate some driver memory. */
  522.  
  523.     if(AudioBlock = (struct IOAudio *)AllocMem(sizeof(struct IOAudio),MEMF_PUBLIC | MEMF_CLEAR))
  524.     {
  525.             /* Time for a replyport? */
  526.  
  527.         if(ReplyPort = (struct MsgPort *)CreatePort(NULL,0))
  528.         {
  529.             AudioBlock -> ioa_Request . io_Message . mn_ReplyPort = ReplyPort;
  530.  
  531.             if(!OpenDevice(AUDIONAME,0,AudioBlock,0))
  532.             {
  533.                     /* Set up initial driver data. */
  534.  
  535.                 AudioBlock -> ioa_Request . io_Command                = ADCMD_ALLOCATE;
  536.                 AudioBlock -> ioa_Request . io_Message . mn_Node . ln_Pri    = 90;
  537.                 AudioBlock -> ioa_Data                        = &AnyChannel[0];
  538.                 AudioBlock -> ioa_Length                    = 4;
  539.  
  540.                 if(!DoIO(AudioBlock))
  541.                 {
  542.                     AudioBlock -> ioa_Request . io_Command    = CMD_WRITE;
  543.                     AudioBlock -> ioa_Request . io_Flags    = ADIOF_PERVOL;
  544.                     AudioBlock -> ioa_Period        = 223;
  545.                     AudioBlock -> ioa_Volume        = 64 / 2 * Volume;
  546.                     AudioBlock -> ioa_Cycles        = 150;
  547.                     AudioBlock -> ioa_Data            = &SineWave[0];
  548.                     AudioBlock -> ioa_Length        = 8;
  549.  
  550.                         /* Beeep! */
  551.  
  552.                     BeginIO(AudioBlock);
  553.                     WaitIO(AudioBlock);
  554.                 }
  555.  
  556.                 CloseDevice(AudioBlock);
  557.             }
  558.  
  559.             DeletePort(ReplyPort);
  560.         }
  561.  
  562.         FreeMem(AudioBlock,sizeof(struct IOAudio));
  563.     }
  564. }
  565.  
  566.     /* VideoBeep(Screen,Perform):
  567.      *
  568.      *    Handles the visual part of the DisplayBeep,
  569.      *    flashes a particular screen or restores its
  570.      *    original colour (well, hope so).
  571.      */
  572.  
  573. VOID
  574. VideoBeep(struct Screen *Screen,BYTE Perform)
  575. {
  576.     UBYTE R,G,B;
  577.  
  578.         /* Beep this screen? */
  579.  
  580.     if(Perform)
  581.     {
  582.             /* Is it already beeping? */
  583.  
  584.         if(!(Screen -> Flags & BEEPING))
  585.         {
  586.                 /* This one's beeping. */
  587.  
  588.             Screen -> Flags |= BEEPING;
  589.  
  590.                 /* Don't forget this one. */
  591.  
  592.             Screen -> SaveColor0 = GetRGB4(Screen -> ViewPort . ColorMap,0);
  593.  
  594.                 /* Reverse the colour. */
  595.  
  596.             R = ((Screen -> SaveColor0 >> 8) & 0xF) ^ 0xF;
  597.             G = ((Screen -> SaveColor0 >> 4) & 0xF) ^ 0xF;
  598.             B = ((Screen -> SaveColor0     ) & 0xF) ^ 0xF;
  599.  
  600.                 /* Set it. */
  601.  
  602.             SetRGB4(&Screen -> ViewPort,0,R,G,B);
  603.         }
  604.     }
  605.     else
  606.     {
  607.             /* Is this one beeping? */
  608.  
  609.         if(Screen -> Flags & BEEPING)
  610.         {
  611.                 /* This one isn't beeping any longer. */
  612.  
  613.             Screen -> Flags &= ~BEEPING;
  614.  
  615.                 /* Restore the saved colour. */
  616.  
  617.             R = ((Screen -> SaveColor0 >> 8) & 0xF);
  618.             G = ((Screen -> SaveColor0 >> 4) & 0xF);
  619.             B = ((Screen -> SaveColor0     ) & 0xF);
  620.  
  621.             SetRGB4(&Screen -> ViewPort,0,R,G,B);
  622.         }
  623.     }
  624. }
  625.  
  626.     /* ModifiedDisplayBeep(Screen):
  627.      *
  628.      *    Magic replacement for usual DisplayBeep()
  629.      *    function.
  630.      */
  631.  
  632. VOID
  633. ModifiedDisplayBeep(struct Screen *Screen)
  634. {
  635.         /* Flash a particular screen. */
  636.  
  637.     if(Screen)
  638.     {
  639.         VideoBeep(Screen,TRUE);
  640.  
  641.         AudioBeep(DSeg -> Beep);
  642.  
  643.         VideoBeep(Screen,FALSE);
  644.     }
  645.     else
  646.     {
  647.             /* Flash all screens. */
  648.  
  649.         ULONG IntuiLock;
  650.  
  651.             /* Where's the first one? Has anybody
  652.              * used the LockIBase() function so
  653.              * far (save me)?
  654.              */
  655.  
  656.         IntuiLock = LockIBase(NULL);
  657.  
  658.         Screen = IntuitionBase -> FirstScreen;
  659.  
  660.             /* Walk through the screens flashing them all. */
  661.  
  662.         do
  663.             VideoBeep(Screen,TRUE);
  664.         while(Screen = Screen -> NextScreen);
  665.  
  666.         UnlockIBase(IntuiLock);
  667.  
  668.             /* Let it resound. */
  669.  
  670.         AudioBeep(DSeg -> Beep);
  671.  
  672.             /* Again: where's the first screen? */
  673.  
  674.         IntuiLock = LockIBase(NULL);
  675.  
  676.         Screen = IntuitionBase -> FirstScreen;
  677.  
  678.         do
  679.             VideoBeep(Screen,FALSE);
  680.         while(Screen = Screen -> NextScreen);
  681.  
  682.         UnlockIBase(IntuiLock);
  683.     }
  684. }
  685.  
  686.     /* PrintIt(TimeBuff,CharOffset):
  687.      *
  688.      *    Prints the formatted string into the Workbench title bar.
  689.      */
  690.  
  691. VOID
  692. PrintIt(STRPTR TimeBuff)
  693. {
  694.     BYTE Length = strlen((char *)TimeBuff);
  695.     BYTE Offset = Width - TextLength(DummyRPort,TimeBuff,Length);
  696.  
  697.     SetAPen(DummyRPort,DSeg -> TextColour);
  698.     SetBPen(DummyRPort,DSeg -> BackColour);
  699.  
  700.     SetRast(DummyRPort,1);
  701.  
  702.     Move(DummyRPort,(Offset >= 0 ? Offset : 0),DummyRPort -> Font -> tf_Baseline);
  703.     Text(DummyRPort,TimeBuff,Length);
  704. }
  705.  
  706.     /* Ring():
  707.      *
  708.      *    This one rings the bell of the alarm clock.
  709.      */
  710.  
  711. VOID
  712. Ring(LONG Tea)
  713. {
  714.     struct IOAudio        *AudioBlock;
  715.     struct MsgPort        *ReplyPort;
  716.  
  717.     struct Screen         PublicScreen;
  718.     struct Screen        *FirstOne;
  719.     struct Window        *ClockWindow;
  720.     struct IntuiMessage    *Massage;
  721.  
  722.     ULONG             Class;
  723.     USHORT             Code;
  724.     struct Gadget        *ID;
  725.  
  726.     ULONG             IntuiLock;
  727.     struct View        *ViewLord;
  728.  
  729.     SHORT             DyOffset,PlusY;
  730.     LONG             TimeOut;
  731.  
  732.         /* Remember initial first screen. */
  733.  
  734.     IntuiLock = LockIBase(NULL);
  735.     FirstOne = IntuitionBase -> FirstScreen;
  736.     UnlockIBase(IntuiLock);
  737.  
  738.         /* Knockin' on heaven's door... */
  739.  
  740.     OpenWorkBench();
  741.  
  742.         /* Center the clock window. */
  743.  
  744.     GetScreenData(&PublicScreen,sizeof(struct Screen),WBENCHSCREEN,NULL);
  745.  
  746.     NewClockWindow . LeftEdge    = (PublicScreen . Width - NewClockWindow . Width) / 2;
  747.     NewClockWindow . TopEdge    = (PublicScreen . Height - NewClockWindow . Height) / 2;
  748.  
  749.         /* Open it and paint the background. */
  750.  
  751.     if(!(ClockWindow = (struct Window *)OpenWindow(&NewClockWindow)))
  752.     {
  753.         DisplayBeep(NULL);
  754.         goto Quit;
  755.     }
  756.  
  757.     if(NewKick)
  758.         SetAPen(ClockWindow -> RPort,2);
  759.     else
  760.         SetAPen(ClockWindow -> RPort,1);
  761.  
  762.     RectFill(ClockWindow -> RPort,2,1,ClockWindow -> Width - 3,ClockWindow -> Height - 2);
  763.  
  764.     RefreshGadgets(&ClockGadget[0],ClockWindow,NULL);
  765.  
  766.         /* Adjust the contents of the alarm time string. */
  767.  
  768.     if(Tea)
  769.         SPrintf(ClockTxt[3] . IText,"Alarm time » %2ld:%02ld:%02ld «",DSeg -> AlarmHour,DSeg -> AlarmMinute,DSeg -> AlarmSecond);
  770.     else
  771.         strcpy((char *)ClockTxt[3] . IText,"Countdown elapsed!");
  772.  
  773.     PrintIText(ClockWindow -> RPort,&ClockTxt[0],0,0);
  774.  
  775.         /* Allocate some driver memory. */
  776.  
  777.     if(AudioBlock = (struct IOAudio *)AllocMem(sizeof(struct IOAudio),MEMF_PUBLIC | MEMF_CLEAR))
  778.     {
  779.             /* Time for a replyport? */
  780.  
  781.         if(ReplyPort = (struct MsgPort *)CreatePort(NULL,0))
  782.         {
  783.                 /* Set up initial driver data. */
  784.  
  785.             AudioBlock -> ioa_Data                    = &AnyChannel[0];
  786.             AudioBlock -> ioa_Length                = 4;
  787.             AudioBlock -> ioa_Request . io_Message . mn_ReplyPort    = ReplyPort;
  788.             AudioBlock -> ioa_Request . io_Message . mn_Node.ln_Pri    = 80;
  789.  
  790.                 /* Allocate the channels on the fly. */
  791.  
  792.             if(!OpenDevice(AUDIONAME,0,AudioBlock,0))
  793.             {
  794.                 AudioBlock -> ioa_Request . io_Command    = CMD_WRITE;
  795.                 AudioBlock -> ioa_Request . io_Flags    = ADIOF_PERVOL;
  796.                 AudioBlock -> ioa_Period        = 308;
  797.                 AudioBlock -> ioa_Volume        = 64;
  798.                 AudioBlock -> ioa_Cycles        = 1;
  799.                 AudioBlock -> ioa_Data            = (UBYTE *)&RingData[0];
  800.                 AudioBlock -> ioa_Length        = 7326;
  801.  
  802.                 IntuiLock = LockIBase(NULL);
  803.                 ViewLord = &IntuitionBase -> ViewLord;
  804.                 UnlockIBase(IntuiLock);
  805.  
  806.                 DyOffset = ViewLord -> DyOffset;
  807.  
  808.                     /* Ring! */
  809.  
  810.                 BeginIO(AudioBlock);
  811.  
  812.                 WBenchToFront();
  813.  
  814.                 TimeOut = 0;
  815.  
  816.                     /* Ring until somebody clicked our window
  817.                      * or a timeout occurs.
  818.                      */
  819.  
  820.                 FOREVER
  821.                 {
  822.                         /* Cycles already finished. */
  823.  
  824.                     if(CheckIO(AudioBlock))
  825.                         BeginIO(AudioBlock);
  826.  
  827.                     Class = Code = NULL;
  828.  
  829.                     if(Massage = GetMsg(ClockWindow -> UserPort))
  830.                     {
  831.                         Class = Massage -> Class;
  832.                         Code  = Massage -> Code;
  833.                         ID    = (struct Gadget *)Massage -> IAddress;
  834.  
  835.                         ReplyMsg(Massage);
  836.  
  837.                         if((Class == GADGETUP && !ID -> GadgetID) || Class == VANILLAKEY)
  838.                             break;
  839.                     }
  840.  
  841.                     PlusY = 1 - RangeRand(3);
  842.  
  843.                         /* Make the view vibrate. */
  844.  
  845.                     if(DyOffset + PlusY >= 0)
  846.                         ViewLord -> DyOffset = DyOffset + PlusY;
  847.  
  848.                     RethinkDisplay();
  849.  
  850.                         /* Wait a tick. */
  851.  
  852.                     Delay(1);
  853.  
  854.                         /* Now for the timeout... */
  855.  
  856.                     if((TimeOut++) >= (TICKS_PER_SECOND * 30))
  857.                         break;
  858.                 }
  859.  
  860.                 ViewLord -> DyOffset = DyOffset;
  861.                 RethinkDisplay();
  862.  
  863.                     /* Still ringing? */
  864.  
  865.                 if(!CheckIO(AudioBlock))
  866.                     WaitIO(AudioBlock);
  867.  
  868.                     /* Tick! */
  869.  
  870.                 CloseDevice(AudioBlock);
  871.             }
  872.  
  873.             DeletePort(ReplyPort);
  874.         }
  875.  
  876.         FreeMem(AudioBlock,sizeof(struct IOAudio));
  877.     }
  878.  
  879.         /* Bring original first screen to the front again. */
  880.  
  881. Quit:    if(FirstOne != ClockWindow -> WScreen)
  882.         ScreenToBack(ClockWindow -> WScreen);
  883.  
  884.     if(ClockWindow)
  885.         CloseWindow(ClockWindow);
  886. }
  887.  
  888.     /* ShowTime(ReallyDoIt,Force):
  889.      *
  890.      *    Yes, it's Showtime! This one compiles the date/timestring
  891.      *    and prints it.
  892.      */
  893.  
  894. VOID
  895. ShowTime(UBYTE ReallyDoIt,UBYTE Force)
  896. {
  897.     UBYTE TempBuff[30];
  898.  
  899.     static char *Months[12] =
  900.     {
  901.         "Jan","Feb","Mar",
  902.         "Apr","May","Jun",
  903.         "Jul","Aug","Sep",
  904.         "Oct","Nov","Dec"
  905.     };
  906.  
  907.     static char *Days[7] =
  908.     {
  909.         "Sun",    /* Note: these have to appear right in this order. */
  910.         "Mon",
  911.         "Tue",
  912.         "Wed",
  913.         "Thu",
  914.         "Fri",
  915.         "Sat"
  916.     };
  917.  
  918.     static char *LongDays[7] =
  919.     {
  920.         "Sunday",
  921.         "Monday",
  922.         "Tuesday",
  923.         "Wednesday",
  924.         "Thursday",
  925.         "Friday",
  926.         "Saturday"
  927.     };
  928.  
  929.     static char DateBuff[15],TimeBuff[10];
  930.  
  931.     static UBYTE LastState = 2;
  932.  
  933.     static SHORT MonthVectors[14] =
  934.     {
  935.         -1, -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364
  936.     };
  937.  
  938.     LONG JulianDate, Day0, Day1, Day2, Day3;
  939.     LONG Year, Month, Day;
  940.  
  941.     struct DateStamp Date;
  942.  
  943.     DateStamp(&Date);
  944.  
  945.     JulianDate = Date . ds_Days + DDELTA;
  946.  
  947.     Year = (JulianDate / 146097) * 400;
  948.  
  949.     Day0 = Day1 = JulianDate %= 146097;
  950.  
  951.     Year += (JulianDate / 36524) * 100;
  952.  
  953.     Day2 = Day1 %= 36524;
  954.  
  955.     Year += (Day2 / 1461) * 4;
  956.  
  957.     Day3 = Day1 %= 1461;
  958.  
  959.     Year += Day3 / 365;
  960.  
  961.     Month = 1 + (Day1 %= 365);
  962.  
  963.     Day = Month % 30;
  964.  
  965.     Month /= 30;
  966.  
  967.     if((Day3 >= 59 && Day0 < 59) || (Day3 < 59 && (Day2 >= 59 || Day0 < 59)))
  968.         Day1++;
  969.  
  970.     if(Day1 > MonthVectors[1 + Month])
  971.         Month++;
  972.  
  973.     Day = Day1 - MonthVectors[Month];
  974.  
  975.     DSeg -> CurrentTime . Year    = Year;
  976.     DSeg -> CurrentTime . Month    = Month;
  977.     DSeg -> CurrentTime . Day    = Day;
  978.     DSeg -> CurrentTime . Weekday    = Date . ds_Days % DAYS_PER_WEEK;
  979.  
  980.     DSeg -> CurrentTime . Hour    = Date . ds_Minute / MINS_PER_HOUR;
  981.     DSeg -> CurrentTime . Minute    = Date . ds_Minute % MINS_PER_HOUR;
  982.     DSeg -> CurrentTime . Second    = Date . ds_Tick   / TICS_PER_SEC;
  983.  
  984.         /* Did we need a change? */
  985.  
  986.     if(LastState != DSeg -> Seconds)
  987.     {
  988.         LastState = DSeg -> Seconds;
  989.         Force = TRUE;
  990.     }
  991.  
  992.     if(ReallyDoIt)
  993.     {
  994.         if(DSeg -> Seconds)
  995.         {
  996.             SPrintf(TempBuff,"%s %02ld-%s-%02ld %02ld:%02ld:%02ld",
  997.                 Days[DSeg -> CurrentTime . Weekday],
  998.                 DSeg -> CurrentTime . Day,
  999.                 Months[DSeg -> CurrentTime . Month - 1],
  1000.                 DSeg -> CurrentTime . Year % 100,
  1001.                 DSeg -> CurrentTime . Hour,
  1002.                 DSeg -> CurrentTime . Minute,
  1003.                 DSeg -> CurrentTime . Second);
  1004.  
  1005.             PrintIt(TempBuff);
  1006.         }
  1007.         else
  1008.         {
  1009.             if(!(Date . ds_Tick / TICKS_PER_SECOND) || Force)
  1010.             {
  1011.                 SPrintf(TempBuff,"%s %02ld-%s-%02ld %02ld:%02ld",
  1012.                     Days[DSeg -> CurrentTime . Weekday],
  1013.                     DSeg -> CurrentTime . Day,
  1014.                     Months[DSeg -> CurrentTime . Month - 1],
  1015.                     DSeg -> CurrentTime . Year % 100,
  1016.                     DSeg -> CurrentTime . Hour,
  1017.                     DSeg -> CurrentTime . Minute);
  1018.  
  1019.                 SetRast(DummyRPort,1);
  1020.  
  1021.                 PrintIt(TempBuff);
  1022.             }
  1023.         }
  1024.     }
  1025.  
  1026.     if((!DSeg -> CurrentTime . Second || !Printed) && DSeg -> SetEnv)
  1027.     {
  1028.         Printed = TRUE;
  1029.  
  1030.         SPrintf(DateBuff,"%02ld-%s-%02ld",Day,Months[DSeg -> CurrentTime . Month - 1],DSeg -> CurrentTime . Year % 100);
  1031.         SPrintf(TimeBuff,"%02ld:%02ld",DSeg -> CurrentTime . Hour,DSeg -> CurrentTime . Minute);
  1032.  
  1033.         Setenv("DAY",LongDays[DSeg -> CurrentTime . Weekday]);
  1034.         Setenv("DATE",DateBuff);
  1035.         Setenv("TIME",TimeBuff);
  1036.     }
  1037. }
  1038.  
  1039.     /* MaxMemSize(MemType):
  1040.      *
  1041.      *    Returns the length of memory block of a special
  1042.      *    kind. Borrowed from Louis A. Mamakos' GfxMem 0.4.
  1043.      */
  1044.  
  1045. ULONG
  1046. MaxMemSize(ULONG MemType)
  1047. {
  1048.     ULONG             BlockSize = 0;
  1049.     struct MemHeader    *MemHeader;
  1050.  
  1051.     Forbid();
  1052.  
  1053.         /* Walk through the memory lists adding the
  1054.          * amount of memory bound to them.
  1055.          */
  1056.  
  1057.     for(MemHeader = (struct MemHeader *)SysBase -> MemList . lh_Head ; MemHeader -> mh_Node . ln_Succ ; MemHeader = (struct MemHeader *)MemHeader -> mh_Node . ln_Succ)
  1058.     {
  1059.         if(MemHeader -> mh_Attributes & MemType)
  1060.             BlockSize += ((ULONG)MemHeader -> mh_Upper - (ULONG)MemHeader -> mh_Lower);
  1061.     }
  1062.  
  1063.     Permit();
  1064.  
  1065.     return(BlockSize);
  1066. }
  1067.  
  1068.     /* FindTheBench():
  1069.      *
  1070.      *    Tries to locate the Workbench screen in the linked list
  1071.      *    of system screens. This is rather a rude method and should
  1072.      *    be exercised only while Intuition is locked.
  1073.      *
  1074.      *    This could be lot easier if using LockPubScreen().
  1075.      */
  1076.  
  1077. struct Screen *
  1078. FindTheBench()
  1079. {
  1080.     struct Screen    *WBench;
  1081.     ULONG         IntuiLock = LockIBase(NULL);
  1082.  
  1083.         /* Start with the first one. */
  1084.  
  1085.     WBench = IntuitionBase -> FirstScreen;
  1086.  
  1087.         /* Scan the list... */
  1088.  
  1089.     do
  1090.     {
  1091.             /* The type we want? */
  1092.  
  1093.         if((WBench -> Flags & SCREENTYPE) == WBENCHSCREEN)
  1094.         {
  1095.             UnlockIBase(IntuiLock);
  1096.             return(WBench);
  1097.         }
  1098.     }
  1099.     while(WBench = WBench -> NextScreen);
  1100.  
  1101.         /* Failed! */
  1102.  
  1103.     UnlockIBase(IntuiLock);
  1104.  
  1105.     return(NULL);
  1106. }
  1107.  
  1108.     /* PlayChime():
  1109.      *
  1110.      *    Plays the hour chime.
  1111.      */
  1112.  
  1113. LONG
  1114. PlayChime()
  1115. {
  1116.     LONG Rate = 447,Length = 8,Cycles = 150,Volume = 64 / 2;
  1117.  
  1118.     if(ChimeReplyPort)
  1119.         return(FALSE);
  1120.  
  1121.     ObtainSemaphore(DSeg -> SoundSemaphore);
  1122.  
  1123.     if(DSeg -> SoundData && DSeg -> SoundLength)
  1124.     {
  1125.         ChimeWaveMap    = (UBYTE *)DSeg -> SoundData;
  1126.         Length        = DSeg -> SoundLength;
  1127.         Volume        = DSeg -> SoundVolume;
  1128.         Rate        = DSeg -> SoundRate;
  1129.         Cycles        = 1;
  1130.     }
  1131.     else
  1132.         ChimeWaveMap = &SineWave[0];
  1133.  
  1134.         /* Allocate some driver memory. */
  1135.  
  1136.     if(!(ChimeAudioBlock = (struct IOAudio *)AllocMem(sizeof(struct IOAudio),MEMF_PUBLIC | MEMF_CLEAR)))
  1137.     {
  1138.         ReleaseSemaphore(DSeg -> SoundSemaphore);
  1139.         return(FALSE);
  1140.     }
  1141.  
  1142.         /* Time for a replyport? */
  1143.  
  1144.     if(!(ChimeReplyPort = (struct MsgPort *)CreatePort(NULL,0)))
  1145.     {
  1146.         FreeMem(ChimeAudioBlock,sizeof(struct IOAudio));
  1147.  
  1148.         ChimeAudioBlock = NULL;
  1149.  
  1150.         ReleaseSemaphore(DSeg -> SoundSemaphore);
  1151.         return(FALSE);
  1152.     }
  1153.  
  1154.         /* Set up initial driver data. */
  1155.  
  1156.     ChimeAudioBlock -> ioa_Data                    = &AnyChannel[0];
  1157.     ChimeAudioBlock -> ioa_Length                    = 4;
  1158.     ChimeAudioBlock -> ioa_Request . io_Message . mn_ReplyPort    = ChimeReplyPort;
  1159.     ChimeAudioBlock -> ioa_Request . io_Message . mn_Node . ln_Pri    = 90;
  1160.  
  1161.         /* Allocate the channels on the fly. */
  1162.  
  1163.     if(OpenDevice(AUDIONAME,0,ChimeAudioBlock,0))
  1164.     {
  1165.         FreeMem(ChimeAudioBlock,sizeof(struct IOAudio));
  1166.         DeletePort(ChimeReplyPort);
  1167.  
  1168.         ChimeAudioBlock    = NULL;
  1169.         ChimeReplyPort    = NULL;
  1170.  
  1171.         ReleaseSemaphore(DSeg -> SoundSemaphore);
  1172.         return(FALSE);
  1173.     }
  1174.  
  1175.     ChimeAudioBlock -> ioa_Request . io_Command    = CMD_WRITE;
  1176.     ChimeAudioBlock -> ioa_Request . io_Flags    = ADIOF_PERVOL;
  1177.     ChimeAudioBlock -> ioa_Cycles            = Cycles;
  1178.     ChimeAudioBlock -> ioa_Data            = ChimeWaveMap;
  1179.     ChimeAudioBlock -> ioa_Length            = Length;
  1180.  
  1181.     ChimeAudioBlock -> ioa_Period            = Rate;
  1182.     ChimeAudioBlock -> ioa_Volume            = Volume;
  1183.  
  1184.     BeginIO(ChimeAudioBlock);
  1185. }
  1186.  
  1187.     /* StopChime():
  1188.      *
  1189.      *    Stops the hour chime.
  1190.      */
  1191.  
  1192. VOID
  1193. StopChime()
  1194. {
  1195.     if(ChimeAudioBlock)
  1196.     {
  1197.         if(ChimeAudioBlock -> ioa_Request . io_Device)
  1198.         {
  1199.             if(!CheckIO(ChimeAudioBlock))
  1200.                 WaitIO(ChimeAudioBlock);
  1201.  
  1202.             CloseDevice(ChimeAudioBlock);
  1203.         }
  1204.  
  1205.         if(!DSeg -> SoundData || !DSeg -> SoundLength)
  1206.         {
  1207.             if(ChimeWaveMap != &SineWave[0])
  1208.                 FreeMem(ChimeWaveMap,8);
  1209.         }
  1210.  
  1211.         FreeMem(ChimeAudioBlock,sizeof(struct IOAudio));
  1212.         DeletePort(ChimeReplyPort);
  1213.  
  1214.         ChimeAudioBlock    = NULL;
  1215.         ChimeReplyPort    = NULL;
  1216.  
  1217.         ReleaseSemaphore(DSeg -> SoundSemaphore);
  1218.     }
  1219. }
  1220.  
  1221.     /* FindChunk(ChunkName,FileHandle):
  1222.      *
  1223.      *    Tries to locate an iff-chunk inside a file.
  1224.      */
  1225.  
  1226. LONG
  1227. FindChunk(ULONG ChunkName,BPTR FileHandle)
  1228. {
  1229.     LONG     OldPosition;
  1230.     ULONG    FormType = 0;
  1231.  
  1232.         /* The format of a typical IFF-chunk. */
  1233.  
  1234.     struct
  1235.     {
  1236.         ULONG    IFF_Type;
  1237.         ULONG    IFF_Length;
  1238.     } Chunk;
  1239.  
  1240.         /* Remember initial file position. */
  1241.  
  1242.     OldPosition = Seek(FileHandle,0,OFFSET_CURRENT);
  1243.  
  1244.         /* Try to find it. */
  1245.  
  1246.     FOREVER
  1247.     {
  1248.             /* Read the first bytes. */
  1249.  
  1250.         if(Read(FileHandle,&Chunk,sizeof(Chunk)) != sizeof(Chunk))
  1251.         {
  1252.             Seek(FileHandle,OldPosition,OFFSET_BEGINNING);
  1253.             return(FALSE);
  1254.         }
  1255.  
  1256.             /* Is it a FORM-chunk? */
  1257.  
  1258.         if(OldPosition == 0 && FormType == 0 && Chunk . IFF_Type == 'FORM')
  1259.         {
  1260.             Read(FileHandle,&FormType,sizeof(LONG));
  1261.  
  1262.                 /* Check the form type. */
  1263.  
  1264.             if(FormType == ChunkName)
  1265.                 return(TRUE);
  1266.  
  1267.             continue;
  1268.         }
  1269.  
  1270.             /* Is it the chunk type we want? */
  1271.  
  1272.         if(Chunk . IFF_Type == ChunkName)
  1273.             return(TRUE);
  1274.  
  1275.             /* Skip chunk. */
  1276.  
  1277.         Seek(FileHandle,Chunk . IFF_Length,OFFSET_CURRENT);
  1278.     }
  1279. }
  1280.  
  1281.     /* LoadChimeSound(Name):
  1282.      *
  1283.      *    Loads an IFF-8SVX-soundfile to be used as the hour
  1284.      *    chime sound.
  1285.      */
  1286.  
  1287. LONG
  1288. LoadChimeSound(char *Name)
  1289. {
  1290.     BPTR SoundHandle;
  1291.  
  1292.     APTR SoundData;
  1293.     LONG SoundRate,SoundLength,SoundVolume;
  1294.  
  1295.         /* The format of the VoiceHeader-chunk. */
  1296.  
  1297.     struct
  1298.     {
  1299.         ULONG    oneShotHiSamples,
  1300.             repeatHiSamples,
  1301.             samplesPerHiCycle;
  1302.         UWORD    samplesPerSec;
  1303.         UBYTE    ctOctave,
  1304.             sCompression;
  1305.         LONG    volume;
  1306.     } VoiceHeader;
  1307.  
  1308.         /* No name? Reset to defaults. */
  1309.  
  1310.     if(!RexxStrCmp(Name,"OFF"))
  1311.     {
  1312.         ObtainSemaphore(DSeg -> SoundSemaphore);
  1313.  
  1314.             /* Free the memory. */
  1315.  
  1316.         if(DSeg -> SoundLength && DSeg -> SoundData)
  1317.             FreeMem(DSeg -> SoundData,DSeg -> SoundLength);
  1318.  
  1319.             /* Mark sound slot as vacant. */
  1320.  
  1321.         DSeg -> SoundLength    = 0;
  1322.         DSeg -> SoundData    = NULL;
  1323.  
  1324.         ReleaseSemaphore(DSeg -> SoundSemaphore);
  1325.  
  1326.         return(TRUE);
  1327.     }
  1328.  
  1329.         /* Open the file for reading. */
  1330.  
  1331.     if(!(SoundHandle = Open(Name,MODE_OLDFILE)))
  1332.         return(FALSE);
  1333.  
  1334.         /* Is it a sound file? */
  1335.  
  1336.     if(!FindChunk('8SVX',SoundHandle))
  1337.     {
  1338.         Close(SoundHandle);
  1339.  
  1340.         return(FALSE);
  1341.     }
  1342.  
  1343.         /* Look for the VoiceHeader. */
  1344.  
  1345.     if(!FindChunk('VHDR',SoundHandle))
  1346.     {
  1347.         Close(SoundHandle);
  1348.         return(FALSE);
  1349.     }
  1350.  
  1351.         /* Read the header. */
  1352.  
  1353.     if(Read(SoundHandle,&VoiceHeader,sizeof(VoiceHeader)) != sizeof(VoiceHeader))
  1354.     {
  1355.         Close(SoundHandle);
  1356.         return(FALSE);
  1357.     }
  1358.  
  1359.         /* Fill in the more important information. */
  1360.  
  1361.     SoundLength    = VoiceHeader . oneShotHiSamples + VoiceHeader . repeatHiSamples;
  1362.     SoundRate    = ((GfxBase -> DisplayFlags & PAL) ? 3546895 : 3579545) / VoiceHeader . samplesPerSec;
  1363.     SoundVolume    = (VoiceHeader . volume > 64 ? 64 : VoiceHeader . volume);
  1364.  
  1365.         /* Proceed with the body chunk. */
  1366.  
  1367.     if(!FindChunk('BODY',SoundHandle))
  1368.     {
  1369.         Close(SoundHandle);
  1370.         return(FALSE);
  1371.     }
  1372.  
  1373.         /* Allocate space for the sound data. */
  1374.  
  1375.     if(!(SoundData = (APTR)AllocMem(SoundLength,MEMF_PUBLIC | MEMF_CHIP)))
  1376.     {
  1377.         Close(SoundHandle);
  1378.         return(FALSE);
  1379.     }
  1380.  
  1381.         /* Read the sound data. */
  1382.  
  1383.     if(Read(SoundHandle,SoundData,SoundLength) != SoundLength)
  1384.     {
  1385.         FreeMem(SoundData,SoundLength);
  1386.         Close(SoundHandle);
  1387.         return(FALSE);
  1388.     }
  1389.  
  1390.         /* Close the file. */
  1391.  
  1392.     Close(SoundHandle);
  1393.  
  1394.         /* Lock access to sound data. */
  1395.  
  1396.     ObtainSemaphore(DSeg -> SoundSemaphore);
  1397.  
  1398.         /* Free last sound. */
  1399.  
  1400.     if(DSeg -> SoundLength && DSeg -> SoundData)
  1401.         FreeMem(DSeg -> SoundData,DSeg -> SoundLength);
  1402.  
  1403.         /* Fill in the data. */
  1404.  
  1405.     DSeg -> SoundData    = SoundData;
  1406.     DSeg -> SoundLength    = SoundLength;
  1407.     DSeg -> SoundRate    = SoundRate;
  1408.     DSeg -> SoundVolume    = SoundVolume;
  1409.  
  1410.         /* Unlock it again. */
  1411.  
  1412.     ReleaseSemaphore(DSeg -> SoundSemaphore);
  1413.  
  1414.     return(TRUE);
  1415. }
  1416.  
  1417.     /* CheckDClockStatus(Arg):
  1418.      *
  1419.      *    Checks DClock option flags and returns them.
  1420.      */
  1421.  
  1422. STRPTR
  1423. CheckDClockStatus(STRPTR Arg)
  1424. {
  1425.         /* Static result string. */
  1426.  
  1427.     STATIC UBYTE Response[200];
  1428.  
  1429.         /* Every option to be checked. */
  1430.  
  1431.     STATIC STRPTR Options[17] =
  1432.     {
  1433.         "BEEP",
  1434.         "CLICK",
  1435.         "CLICKVOLUME",
  1436.         "PRIORITY",
  1437.         "TEXTCOLOUR",
  1438.         "BACKCOLOUR",
  1439.         "ALARM",
  1440.         "ALARMTIME",
  1441.         "SETENV",
  1442.         "VERSION",
  1443.         "COUNTDOWN",
  1444.         "HOUR",
  1445.         "SECONDS",
  1446.         "SOUND",
  1447.         "PAGE",
  1448.         "SPEECH",
  1449.         "LINE"
  1450.     };
  1451.  
  1452.         /* A temporary string. */
  1453.  
  1454.     UBYTE TempString[20];
  1455.  
  1456.     LONG i,TheOption = -1;
  1457.  
  1458.         /* Clear the string. */
  1459.  
  1460.     Response[0] = 0;
  1461.  
  1462.         /* Check which option matches the argument.
  1463.          * If none matches we'll produce a string
  1464.          * filled with all options.
  1465.          */
  1466.  
  1467.     for(i = 0 ; i < 16 ; i++)
  1468.     {
  1469.         if(!RexxStrCmp(Arg,Options[i]))
  1470.         {
  1471.             TheOption = i;
  1472.             break;
  1473.         }
  1474.     }
  1475.  
  1476.         /* Is it BEEP? */
  1477.  
  1478.     if(TheOption == 0 || TheOption == -1)
  1479.     {
  1480.         if(TheOption == -1)
  1481.         {
  1482.             strcat(Response,Options[0]);
  1483.             strcat(Response," ");
  1484.         }
  1485.  
  1486.         strcat(Response,(DSeg -> Beep ? "ON" : "OFF"));
  1487.     }
  1488.  
  1489.         /* Is it CLICK? */
  1490.  
  1491.     if(TheOption == 1 || TheOption == -1)
  1492.     {
  1493.         if(TheOption == -1)
  1494.         {
  1495.             strcat(Response," ");
  1496.             strcat(Response,Options[1]);
  1497.             strcat(Response," ");
  1498.         }
  1499.  
  1500.         strcat(Response,(DSeg -> Click ? "ON" : "OFF"));
  1501.     }
  1502.  
  1503.         /* Is it CLICKVOLUME? */
  1504.  
  1505.     if(TheOption == 2 || TheOption == -1)
  1506.     {
  1507.         if(TheOption == -1)
  1508.         {
  1509.             strcat(Response," ");
  1510.             strcat(Response,Options[2]);
  1511.             strcat(Response," ");
  1512.         }
  1513.  
  1514.         BuildValueString(DSeg -> ClickVolume,TempString);
  1515.  
  1516.         strcat(Response,TempString);
  1517.     }
  1518.  
  1519.         /* Is it PRIORITY? */
  1520.  
  1521.     if(TheOption == 3 || TheOption == -1)
  1522.     {
  1523.         if(TheOption == -1)
  1524.         {
  1525.             strcat(Response," ");
  1526.             strcat(Response,Options[3]);
  1527.             strcat(Response," ");
  1528.         }
  1529.  
  1530.         BuildValueString(DSeg -> Priority,TempString);
  1531.  
  1532.         strcat(Response,TempString);
  1533.     }
  1534.  
  1535.         /* Is it TEXTCOLOUR? */
  1536.  
  1537.     if(TheOption == 4 || TheOption == -1)
  1538.     {
  1539.         if(TheOption == -1)
  1540.         {
  1541.             strcat(Response," ");
  1542.             strcat(Response,Options[4]);
  1543.             strcat(Response," ");
  1544.         }
  1545.  
  1546.         BuildValueString(DSeg -> TextColour,TempString);
  1547.  
  1548.         strcat(Response,TempString);
  1549.     }
  1550.  
  1551.         /* Is it BACKCOLOUR? */
  1552.  
  1553.     if(TheOption == 5 || TheOption == -1)
  1554.     {
  1555.         if(TheOption == -1)
  1556.         {
  1557.             strcat(Response," ");
  1558.             strcat(Response,Options[5]);
  1559.             strcat(Response," ");
  1560.         }
  1561.  
  1562.         BuildValueString(DSeg -> Priority,TempString);
  1563.  
  1564.         strcat(Response,TempString);
  1565.     }
  1566.  
  1567.         /* Is it ALARM? */
  1568.  
  1569.     if(TheOption == 6 || TheOption == -1)
  1570.     {
  1571.         if(TheOption == -1)
  1572.         {
  1573.             strcat(Response," ");
  1574.             strcat(Response,Options[6]);
  1575.             strcat(Response," ");
  1576.         }
  1577.  
  1578.         strcat(Response,(DSeg -> Alarm ? "ON" : "OFF"));
  1579.     }
  1580.  
  1581.         /* Is it ALARMTIME? */
  1582.  
  1583.     if(TheOption == 7 || TheOption == -1)
  1584.     {
  1585.         if(TheOption == -1)
  1586.         {
  1587.             strcat(Response," ");
  1588.             strcat(Response,Options[7]);
  1589.             strcat(Response," ");
  1590.         }
  1591.  
  1592.         SPrintf(TempString,"%02ld:%02ld:%02ld",DSeg -> AlarmHour,DSeg -> AlarmMinute,DSeg -> AlarmSecond);
  1593.  
  1594.         strcat(Response,TempString);
  1595.     }
  1596.  
  1597.         /* Is it SETENV? */
  1598.  
  1599.     if(TheOption == 8 || TheOption == -1)
  1600.     {
  1601.         if(TheOption == -1)
  1602.         {
  1603.             strcat(Response," ");
  1604.             strcat(Response,Options[8]);
  1605.             strcat(Response," ");
  1606.         }
  1607.  
  1608.         strcat(Response,(DSeg -> SetEnv ? "ON" : "OFF"));
  1609.     }
  1610.  
  1611.         /* Is it VERSION? */
  1612.  
  1613.     if(TheOption == 9 || TheOption == -1)
  1614.     {
  1615.         if(TheOption == -1)
  1616.         {
  1617.             strcat(Response," ");
  1618.             strcat(Response,Options[9]);
  1619.             strcat(Response," ");
  1620.         }
  1621.  
  1622.         BuildValueString(DSeg -> Revision,TempString);
  1623.  
  1624.         strcat(Response,TempString);
  1625.     }
  1626.  
  1627.         /* Is it COUNTDOWN? */
  1628.  
  1629.     if(TheOption == 10 || TheOption == -1)
  1630.     {
  1631.         if(TheOption == -1)
  1632.         {
  1633.             strcat(Response," ");
  1634.             strcat(Response,Options[10]);
  1635.             strcat(Response," ");
  1636.         }
  1637.  
  1638.         BuildValueString(DSeg -> Countdown,TempString);
  1639.  
  1640.         strcat(Response,TempString);
  1641.     }
  1642.  
  1643.         /* Is it HOUR? */
  1644.  
  1645.     if(TheOption == 11 || TheOption == -1)
  1646.     {
  1647.         if(TheOption == -1)
  1648.         {
  1649.             strcat(Response," ");
  1650.             strcat(Response,Options[11]);
  1651.             strcat(Response," ");
  1652.         }
  1653.  
  1654.         strcat(Response,(DSeg -> Hour ? "ON" : "OFF"));
  1655.     }
  1656.  
  1657.         /* Is it SECONDS? */
  1658.  
  1659.     if(TheOption == 12 || TheOption == -1)
  1660.     {
  1661.         if(TheOption == -1)
  1662.         {
  1663.             strcat(Response," ");
  1664.             strcat(Response,Options[12]);
  1665.             strcat(Response," ");
  1666.         }
  1667.  
  1668.         strcat(Response,(DSeg -> Seconds ? "ON" : "OFF"));
  1669.     }
  1670.  
  1671.         /* Is it SOUND? */
  1672.  
  1673.     if(TheOption == 13 || TheOption == -1)
  1674.     {
  1675.         if(TheOption == -1)
  1676.         {
  1677.             strcat(Response," ");
  1678.             strcat(Response,Options[13]);
  1679.             strcat(Response," ");
  1680.         }
  1681.  
  1682.         strcat(Response,((DSeg -> SoundData && DSeg -> SoundLength) ? "ON" : "OFF"));
  1683.     }
  1684.  
  1685.         /* Is it PAGE? */
  1686.  
  1687.     if(TheOption == 14 || TheOption == -1)
  1688.     {
  1689.         if(TheOption == -1)
  1690.         {
  1691.             strcat(Response," ");
  1692.             strcat(Response,Options[14]);
  1693.             strcat(Response," ");
  1694.         }
  1695.  
  1696.         BuildValueString(DSeg -> Page,TempString);
  1697.  
  1698.         strcat(Response,TempString);
  1699.     }
  1700.  
  1701.         /* Is it SPEECH? */
  1702.  
  1703.     if(TheOption == 15 || TheOption == -1)
  1704.     {
  1705.         if(TheOption == -1)
  1706.         {
  1707.             strcat(Response," ");
  1708.             strcat(Response,Options[15]);
  1709.             strcat(Response," ");
  1710.         }
  1711.  
  1712.         strcat(Response,(DSeg -> Speech ? "ON" : "OFF"));
  1713.     }
  1714.  
  1715.         /* Is it LINE? */
  1716.  
  1717.     if(TheOption == 16 || TheOption == -1)
  1718.     {
  1719.         if(TheOption == -1)
  1720.         {
  1721.             strcat(Response," ");
  1722.             strcat(Response,Options[16]);
  1723.             strcat(Response," ");
  1724.         }
  1725.  
  1726.         strcat(Response,(DSeg -> Online ? "ON" : "OFF"));
  1727.     }
  1728.  
  1729.         /* Return the result of our 'questionnaire'. */
  1730.  
  1731.     return(Response);
  1732. }
  1733.  
  1734.     /* RexxServer():
  1735.      *
  1736.      *    Rexx server subtask (process), handles the rexx commands
  1737.      *    asynchronously.
  1738.      */
  1739.  
  1740. VOID
  1741. RexxServer()
  1742. {
  1743.     struct RexxMessage    *RexxMsg;    /* Rexx signal message. */
  1744.     ULONG             SignalSet;    /* Incoming signals. */
  1745.  
  1746.     STRPTR             StringResult;    /* Result string (error message). */
  1747.     LONG             NumResult;    /* Return code. */
  1748.  
  1749.     LONG             ArgCount;    /* String counter. */
  1750.  
  1751.     UBYTE Arg1[20],Arg2[20];        /* Both arguments. */
  1752.  
  1753.         /* Get the base register (hey Manx, where's
  1754.          * the __saveds equivalent?).
  1755.          */
  1756.  
  1757.     geta4();
  1758.  
  1759.         /* Create the communication port. */
  1760.  
  1761.     if(!(RexxTaskPort = (struct MsgPort *)CreatePort(NULL,0)))
  1762.         goto Quit;
  1763.  
  1764.         /* Synchronization. */
  1765.  
  1766.     Signal(HandlerProcess,SIG_SHAKE);
  1767.  
  1768.         /* Ad infinitum. */
  1769.  
  1770.     FOREVER
  1771.     {
  1772.             /* Wait for a signal. */
  1773.  
  1774.         SignalSet = Wait(SIG_CLOSE | (1 << RexxTaskPort -> mp_SigBit));
  1775.  
  1776.             /* A rexx message? */
  1777.  
  1778.         if(SignalSet & (1 << RexxTaskPort -> mp_SigBit))
  1779.         {
  1780.                 /* Intercept all messages. */
  1781.  
  1782.             while(RexxMsg = GetMsg(RexxTaskPort))
  1783.             {
  1784.                 ArgCount = 0;
  1785.  
  1786.                     /* Get the command string. */
  1787.  
  1788.                 StringResult = GetRexxCommand(RexxMsg);
  1789.  
  1790.                     /* Get command and argument. */
  1791.  
  1792.                 GetToken(StringResult,&ArgCount,Arg1,20);
  1793.                 GetToken(StringResult,&ArgCount,Arg2,20);
  1794.  
  1795.                     /* Zero default values. */
  1796.  
  1797.                 StringResult    = NULL;
  1798.                 NumResult    = 0;
  1799.  
  1800.                     /* Adjust Click volume? */
  1801.  
  1802.                 if(!RexxStrCmp(Arg1,"CLICKVOLUME"))
  1803.                 {
  1804.                     if(GetStringValue(Arg2) > 64 || GetStringValue(Arg2) < 0)
  1805.                         NumResult = 10;
  1806.                     else
  1807.                         DSeg -> ClickVolume = GetStringValue(Arg2);
  1808.                 }
  1809.  
  1810.                     /* Adjust task priority? */
  1811.  
  1812.                 if(!RexxStrCmp(Arg1,"PRIORITY"))
  1813.                 {
  1814.                     if(GetStringValue(Arg2) > 127 || GetStringValue(Arg2) < -128)
  1815.                         NumResult = 10;
  1816.                     else
  1817.                     {
  1818.                         DSeg -> Priority = GetStringValue(Arg2);
  1819.                         SetTaskPri(DSeg -> Child,DSeg -> Priority);
  1820.                     }
  1821.                 }
  1822.  
  1823.                     /* Turn DisplayBeep() off? */
  1824.  
  1825.                 if(!RexxStrCmp(Arg1,"BEEP"))
  1826.                 {
  1827.                     if(!RexxStrCmp(Arg2,"OFF"))
  1828.                         DSeg -> Beep = FALSE;
  1829.                     else
  1830.                     {
  1831.                         if(!RexxStrCmp(Arg2,"ON"))
  1832.                             DSeg -> Beep = TRUE;
  1833.                         else
  1834.                             NumResult = 10;
  1835.                     }
  1836.                 }
  1837.  
  1838.                     /* Turn keyboard click off? */
  1839.  
  1840.                 if(!RexxStrCmp(Arg1,"CLICK"))
  1841.                 {
  1842.                     if(!RexxStrCmp(Arg2,"OFF"))
  1843.                         DSeg -> Click = FALSE;
  1844.                     else
  1845.                     {
  1846.                         if(!RexxStrCmp(Arg2,"ON"))
  1847.                             DSeg -> Click = TRUE;
  1848.                         else
  1849.                             NumResult = 10;
  1850.                     }
  1851.                 }
  1852.  
  1853.                     /* Set text colour? */
  1854.  
  1855.                 if(!RexxStrCmp(Arg1,"TEXTCOLOUR"))
  1856.                     DSeg -> TextColour = GetStringValue(Arg2);
  1857.  
  1858.                     /* Set background colour? */
  1859.  
  1860.                 if(!RexxStrCmp(Arg1,"BACKCOLOUR"))
  1861.                     DSeg -> BackColour = GetStringValue(Arg2);
  1862.  
  1863.                     /* Turn alarm on? */
  1864.  
  1865.                 if(!RexxStrCmp(Arg1,"ALARM"))
  1866.                 {
  1867.                     if(!RexxStrCmp(Arg2,"OFF"))
  1868.                         DSeg -> Alarm = FALSE;
  1869.                     else
  1870.                     {
  1871.                         if(!RexxStrCmp(Arg2,"ON"))
  1872.                             DSeg -> Alarm = TRUE;
  1873.                         else
  1874.                             NumResult = 10;
  1875.                     }
  1876.                 }
  1877.  
  1878.                     /* Set alarm time? */
  1879.  
  1880.                 if(!RexxStrCmp(Arg1,"ALARMTIME"))
  1881.                 {
  1882.                     char TimeBuff[3];
  1883.                     LONG TheTime;
  1884.  
  1885.                     TimeBuff[2] = 0;
  1886.  
  1887.                     TimeBuff[0] = Arg2[0];
  1888.                     TimeBuff[1] = Arg2[1];
  1889.  
  1890.                     TheTime = GetStringValue(TimeBuff);
  1891.  
  1892.                     if(TheTime >= 0 && TheTime <= 23)
  1893.                         DSeg -> AlarmHour = TheTime;
  1894.                     else
  1895.                         NumResult = 10;
  1896.  
  1897.                     TimeBuff[0] = Arg2[3];
  1898.                     TimeBuff[1] = Arg2[4];
  1899.  
  1900.                     TheTime = GetStringValue(TimeBuff);
  1901.  
  1902.                     if(TheTime >= 0 && TheTime <= 59)
  1903.                         DSeg -> AlarmMinute = TheTime;
  1904.                     else
  1905.                         NumResult = 10;
  1906.  
  1907.                     TimeBuff[0] = Arg2[6];
  1908.                     TimeBuff[1] = Arg2[7];
  1909.  
  1910.                     TheTime = GetStringValue(TimeBuff);
  1911.  
  1912.                     if(TheTime >= 0 && TheTime <= 59)
  1913.                         DSeg -> AlarmSecond = TheTime;
  1914.                     else
  1915.                         NumResult = 10;
  1916.                 }
  1917.  
  1918.                     /* Set environment variables? */
  1919.  
  1920.                 if(!RexxStrCmp(Arg1,"SETENV"))
  1921.                 {
  1922.                     if(!RexxStrCmp(Arg2,"OFF"))
  1923.                         DSeg -> SetEnv = FALSE;
  1924.                     else
  1925.                     {
  1926.                         if(!RexxStrCmp(Arg2,"ON"))
  1927.                             DSeg -> SetEnv = TRUE;
  1928.                         else
  1929.                             NumResult = 10;
  1930.                     }
  1931.                 }
  1932.  
  1933.                     /* Set the countdown tea timer? */
  1934.  
  1935.                 if(!RexxStrCmp(Arg1,"COUNTDOWN"))
  1936.                 {
  1937.                     if(GetStringValue(Arg2) > 0)
  1938.                         DSeg -> Countdown = GetStringValue(Arg2);
  1939.                     else
  1940.                         NumResult = 10;
  1941.                 }
  1942.  
  1943.                     /* Refresh the display? */
  1944.  
  1945.                 if(!RexxStrCmp(Arg1,"REFRESH"))
  1946.                 {
  1947.                     if(CloseWorkBench())
  1948.                         OpenWorkBench();
  1949.                 }
  1950.  
  1951.                     /* Turn hour alarm on? */
  1952.  
  1953.                 if(!RexxStrCmp(Arg1,"HOUR"))
  1954.                 {
  1955.                     if(!RexxStrCmp(Arg2,"OFF"))
  1956.                         DSeg -> Hour = FALSE;
  1957.                     else
  1958.                     {
  1959.                         if(!RexxStrCmp(Arg2,"ON"))
  1960.                             DSeg -> Hour = TRUE;
  1961.                         else
  1962.                             NumResult = 10;
  1963.                     }
  1964.                 }
  1965.  
  1966.                     /* Turn keyboard click off? */
  1967.  
  1968.                 if(!RexxStrCmp(Arg1,"SECONDS"))
  1969.                 {
  1970.                     if(!RexxStrCmp(Arg2,"OFF"))
  1971.                         DSeg -> Seconds = FALSE;
  1972.                     else
  1973.                     {
  1974.                         if(!RexxStrCmp(Arg2,"ON"))
  1975.                             DSeg -> Seconds = TRUE;
  1976.                         else
  1977.                             NumResult = 10;
  1978.                     }
  1979.                 }
  1980.  
  1981.                     /* Load a sound file? */
  1982.  
  1983.                 if(!RexxStrCmp(Arg1,"SOUND"))
  1984.                 {
  1985.                     if(!LoadChimeSound(Arg2))
  1986.                         NumResult = 10;
  1987.                 }
  1988.  
  1989.                     /* Get a DClock status. */
  1990.  
  1991.                 if(!RexxStrCmp(Arg1,"STATUS"))
  1992.                     StringResult = CheckDClockStatus(Arg2);
  1993.  
  1994.                     /* Show a special page? */
  1995.  
  1996.                 if(!RexxStrCmp(Arg1,"PAGE"))
  1997.                 {
  1998.                     if(GetStringValue(Arg2) > 4 || GetStringValue(Arg2) < 0 || (GetStringValue(Arg2) == 4 && DSeg -> Countdown < 1))
  1999.                         NumResult = 10;
  2000.                     else
  2001.                         DSeg -> Page = GetStringValue(Arg2);
  2002.                 }
  2003.  
  2004.                     /* Tell the current time? */
  2005.  
  2006.                 if(!RexxStrCmp(Arg1,"TELLTIME"))
  2007.                 {
  2008.                     if(!SpeechProcess)
  2009.                     {
  2010.                         Forbid();
  2011.  
  2012.                         if(SpeechProcess = CreateFuncProc("DClock-Speech",5,SpeechServer,4000))
  2013.                             Wait(SIG_SHAKE);
  2014.  
  2015.                         Permit();
  2016.                     }
  2017.  
  2018.                     if(SpeechProcess)
  2019.                         Signal(SpeechProcess,SIG_CLICK);
  2020.                 }
  2021.  
  2022.                     /* Read the clock chip? */
  2023.  
  2024.                 if(!RexxStrCmp(Arg1,"READTIME"))
  2025.                 {
  2026.                     if(!ReadClock())
  2027.                         NumResult = 10;
  2028.                 }
  2029.  
  2030.                     /* Reply the rexx command. */
  2031.  
  2032.                 ReplyRexxCommand(RexxMsg,NumResult,0,StringResult);
  2033.             }
  2034.         }
  2035.  
  2036.             /* Remove the server task. */
  2037.  
  2038.         if(SignalSet & SIG_CLOSE)
  2039.             break;
  2040.     }
  2041.  
  2042.         /* Delete the port. */
  2043.  
  2044. Quit:    if(RexxTaskPort)
  2045.         DeletePort(RexxTaskPort);
  2046.  
  2047.         /* Hey, I'm done! */
  2048.  
  2049.     Signal(HandlerProcess,SIG_SHAKE);
  2050. }
  2051.  
  2052.     /* SpeechServer():
  2053.      *
  2054.      *    Asks the narrator device to tell the time.
  2055.      */
  2056.  
  2057. VOID
  2058. SpeechServer()
  2059. {
  2060.     ULONG             SignalSet;        /* Signal mask. */
  2061.     struct MsgPort        *NarratorPort;        /* Narrator reply port. */
  2062.     struct narrator_rb    *NarratorRequest;    /* Narrator device link. */
  2063.  
  2064.     char             WorkString[40];    /* Phoneme string. */
  2065.  
  2066.         /* Numbers above twenty. */
  2067.  
  2068.     static char *AboveTwenty[4] =
  2069.     {
  2070.         "TWEH4NTIY4",
  2071.         "THER4TIY4",
  2072.         "FOH4RTIY4",
  2073.         "FIH4FTIY4"
  2074.     };
  2075.  
  2076.         /* Numbers from zero to nineteen. */
  2077.  
  2078.     static char *SingleNumbers[20] =
  2079.     {
  2080.         "ZIY4ROW",
  2081.         "WAH4N",
  2082.         "TUW4",
  2083.         "THRIY4",
  2084.         "FOH4R",
  2085.         "FAY4V",
  2086.         "SIH4KKSZ",
  2087.         "SEH4VVEHN",
  2088.         "EY4T",
  2089.         "NAY4N",
  2090.         "TEH4N",
  2091.         "IY4LAEEHFAEEHN",
  2092.         "TWEH4LF",
  2093.         "THER4TIY4N",
  2094.         "FOH4RTIY4N",
  2095.         "FIH4FTIY4N",
  2096.         "SIH4KKSZTIY4N",
  2097.         "SEH4VVEHNTIY4N",
  2098.         "EY3TIY4N",
  2099.         "NAY4NTIY4N"
  2100.     };
  2101.  
  2102.         /* The two halves of the day. */
  2103.  
  2104.     static char *TimeOfDay[2] =
  2105.     {
  2106.         " EY5EH3M.",
  2107.         " BPIY5EH3M."
  2108.     };
  2109.  
  2110.         /* Get the data base register. */
  2111.  
  2112.     geta4();
  2113.  
  2114.         /* Allocate speech driver data. */
  2115.  
  2116.     if(NarratorPort = CreatePort(NULL,0))
  2117.     {
  2118.         if(NarratorRequest = (struct narrator_rb *)CreateExtIO(NarratorPort,sizeof(struct narrator_rb)))
  2119.         {
  2120.                 /* Say which channels we need. */
  2121.  
  2122.             NarratorRequest -> ch_masks        = &AnyChannel[0];
  2123.             NarratorRequest -> nm_masks        = 4;
  2124.  
  2125.                 /* This is a write request. */
  2126.  
  2127.             NarratorRequest -> message . io_Command    = CMD_WRITE;
  2128.             NarratorRequest -> message . io_Data    = (APTR)WorkString;
  2129.  
  2130.                 /* Try to open the speech driver. */
  2131.  
  2132.             if(!OpenDevice("narrator.device",0,NarratorRequest,0))
  2133.             {
  2134.                     /* Handshake with handler process. */
  2135.  
  2136.                 Signal(HandlerProcess,SIG_SHAKE);
  2137.  
  2138.                 if(RexxProcess)
  2139.                     Signal(RexxProcess,SIG_SHAKE);
  2140.  
  2141.                 FOREVER
  2142.                 {
  2143.                         /* Wait for signal. */
  2144.  
  2145.                     SignalSet = Wait(SIG_CLOSE | SIG_CLICK);
  2146.  
  2147.                         /* Are we to shut down? */
  2148.  
  2149.                     if(SignalSet & SIG_CLOSE)
  2150.                         break;
  2151.  
  2152.                         /* Prevent time data from being changed. */
  2153.  
  2154.                     Forbid();
  2155.  
  2156.                     strcpy(WorkString,"IH4THZ ");
  2157.  
  2158.                         /* Is the hour below twelve? */
  2159.  
  2160.                     if(DSeg -> CurrentTime . Hour <= 12)
  2161.                         strcat(WorkString,SingleNumbers[DSeg -> CurrentTime . Hour]);
  2162.  
  2163.                         /* Is it above twelve? */
  2164.  
  2165.                     if(DSeg -> CurrentTime . Hour > 12)
  2166.                         strcat(WorkString,SingleNumbers[DSeg -> CurrentTime . Hour - 12]);
  2167.  
  2168.                     strcat(WorkString," EH4KLAA4K ");
  2169.  
  2170.                         /* Are the minutes above zero and below twenty? */
  2171.  
  2172.                     if(DSeg -> CurrentTime . Minute < 20 && DSeg -> CurrentTime . Minute > 0)
  2173.                         strcat(WorkString,SingleNumbers[DSeg -> CurrentTime . Minute]);
  2174.  
  2175.                         /* Are the minutes above twenty? */
  2176.  
  2177.                     if(DSeg -> CurrentTime . Minute >= 20)
  2178.                     {
  2179.                             /* Append the tenths first. */
  2180.  
  2181.                         strcat(WorkString,AboveTwenty[DSeg -> CurrentTime . Minute / 10 - 2]);
  2182.  
  2183.                             /* Add the minutes if any. */
  2184.  
  2185.                         if(DSeg -> CurrentTime . Minute % 10)
  2186.                             strcat(WorkString,SingleNumbers[DSeg -> CurrentTime . Minute % 10]);
  2187.                     }
  2188.  
  2189.                         /* Add a.m./p.m. */
  2190.  
  2191.                     if(DSeg -> CurrentTime . Hour > 12)
  2192.                         strcat(WorkString,TimeOfDay[1]);
  2193.                     else
  2194.                     {
  2195.                         if(DSeg -> CurrentTime . Hour > 0 && DSeg -> CurrentTime . Hour < 12)
  2196.                             strcat(WorkString,TimeOfDay[0]);
  2197.                     }
  2198.  
  2199.                     Permit();
  2200.  
  2201.                         /* Remember the length (-1 doesn't work here for some reason). */
  2202.  
  2203.                     NarratorRequest -> message . io_Length = strlen(WorkString);
  2204.  
  2205.                         /* Say it and wait for it to terminate. */
  2206.  
  2207.                     SendIO(NarratorRequest);
  2208.                     WaitIO(NarratorRequest);
  2209.  
  2210.                     GetMsg(NarratorPort);
  2211.                 }
  2212.  
  2213.                     /* Deallocate our resources and exit. */
  2214.                 
  2215.                 while(!CheckIO(NarratorRequest))
  2216.                 {
  2217.                     AbortIO(NarratorRequest);
  2218.                     WaitIO(NarratorRequest);
  2219.  
  2220.                     GetMsg(NarratorPort);
  2221.                 }
  2222.  
  2223.                 CloseDevice(NarratorRequest);
  2224.             }
  2225.  
  2226.             DeleteExtIO(NarratorRequest);
  2227.         }
  2228.  
  2229.         DeletePort(NarratorPort);
  2230.     }
  2231.  
  2232.         /* Lock & quit. */
  2233.  
  2234.     Forbid();
  2235.  
  2236. /*    SpeechProcess = NULL;*/
  2237.  
  2238.     Signal(HandlerProcess,SIG_SHAKE);
  2239.  
  2240.         /* The following piece of code is a kludge as any
  2241.          * can be. The speech server process is created
  2242.          * by CreateFuncProc() and runs fine but ends up
  2243.          * stumbling across illegal code on exit. My solution
  2244.          * to this problem is to save a pointer to the
  2245.          * current process code, clear out the pointer
  2246.          * for the rest of the handler and to jump into the
  2247.          * RemTask code.
  2248.          */
  2249.  
  2250. #asm
  2251.     xref    _SpeechProcess
  2252.     xref    _LVORemTask
  2253.  
  2254.     moveq    #0,d0
  2255.  
  2256.     move.l    _SpeechProcess,a1
  2257.     move.l    d0,_SpeechProcess
  2258.  
  2259.     move.l    (4).w,a6
  2260.     jmp    _LVORemTask(a6)
  2261. #endasm
  2262. }
  2263.  
  2264.     /* CreateFuncProc():
  2265.      *
  2266.      *    Similar to CreateTask this routine will start a
  2267.      *    'C' function as a process. The technique used
  2268.      *    by this nifty little piece of code was originally
  2269.      *    conceived by Leo Schwab.
  2270.      */
  2271.  
  2272. struct Process *
  2273. CreateFuncProc(char *Name,LONG Priority,APTR InitCode,ULONG StackSize)
  2274. {
  2275.     struct Process *ChildProc = NULL;
  2276.  
  2277.     struct
  2278.     {
  2279.         BPTR    NextSeg;    /* Pointer to next segment. */
  2280.  
  2281.         WORD    FirstCode;    /* First instruction (JMP). */
  2282.         APTR    RealCode;    /* Address of function. */
  2283.     } *FakeSeg;
  2284.  
  2285.         /* Allocate the segment. */
  2286.  
  2287.     if(FakeSeg = (struct FakeSeg *)AllocMem(sizeof(*FakeSeg),MEMF_PUBLIC | MEMF_CLEAR))
  2288.     {
  2289.         struct MsgPort *ChildPort;
  2290.  
  2291.             /* Fill in the data. */
  2292.  
  2293.         FakeSeg -> FirstCode    = 0x4EF9;    /* JMP EA */
  2294.         FakeSeg -> RealCode    = InitCode;    /* EA */
  2295.  
  2296.             /* Create the process. */
  2297.  
  2298.         if(ChildPort = (struct MsgPort *)CreateProc(Name,Priority,MKBADDR(FakeSeg),StackSize))
  2299.             ChildProc = (struct Process *)ChildPort -> mp_SigTask;
  2300.  
  2301.         Delay(TICKS_PER_SECOND);
  2302.  
  2303.             /* Free the segment. */
  2304.  
  2305.         FreeMem(FakeSeg,sizeof(*FakeSeg));
  2306.     }
  2307.  
  2308.         /* Return the pointer to the process. */
  2309.  
  2310.     return(ChildProc);
  2311. }
  2312.  
  2313.     /* CreateDummyRPort():
  2314.      *
  2315.      *    Creates a hidden RastPort for time/graphics
  2316.      *    rendering.
  2317.      */
  2318.  
  2319. BYTE
  2320. CreateDummyRPort()
  2321. {
  2322.     struct TextAttr    FontRequest;
  2323.     SHORT        i;
  2324.  
  2325.         /* Allocate the BitMap. */
  2326.  
  2327.     if(!(DummyMap = (struct DummyMap *)AllocMem(sizeof(struct BitMap),MEMF_PUBLIC | MEMF_CLEAR)))
  2328.         return(FALSE);
  2329.  
  2330.         /* How many bitplanes are there in the display? */
  2331.  
  2332.     DummyDepth = Window -> RPort -> BitMap -> Depth;
  2333.  
  2334.         /* Initialize the bitmap pointers. */
  2335.  
  2336.     InitBitMap(DummyMap,DummyDepth,Width,Height);
  2337.  
  2338.         /* Allocate the memory. */
  2339.  
  2340.     for(i = 0 ; i < DummyDepth ; i++)
  2341.     {
  2342.         if(!(DummyMap -> Planes[i] = AllocMem(Byte(Width) * Height,MEMF_CHIP)))
  2343.             return(FALSE);
  2344.     }
  2345.  
  2346.         /* Mangle the RastPort. */
  2347.  
  2348.     if(!(DummyRPort = (struct RastPort *)AllocMem(sizeof(struct RastPort),MEMF_PUBLIC | MEMF_CLEAR)))
  2349.         return(FALSE);
  2350.  
  2351.         /* Initialize the RastPort. */
  2352.  
  2353.     InitRastPort(DummyRPort);
  2354.  
  2355.         /* Link it to the BitMap. */
  2356.  
  2357.     DummyRPort -> BitMap = DummyMap;
  2358.  
  2359.         /* Clear the drawing area. */
  2360.  
  2361.     SetRast(DummyRPort,1);
  2362.  
  2363.         /* Copy the font attributes. */
  2364.  
  2365.     FontRequest . ta_Name    = (STRPTR)Window -> IFont -> tf_Message . mn_Node . ln_Name;
  2366.     FontRequest . ta_YSize    = Window -> IFont -> tf_YSize;
  2367.     FontRequest . ta_Style    = Window -> IFont -> tf_Style;
  2368.     FontRequest . ta_Flags    = Window -> IFont -> tf_Flags;
  2369.  
  2370.         /* Can we open the font (is it already in the system
  2371.          * list)?
  2372.          */
  2373.  
  2374.     if(!(DummyFont = (struct TextFont *)OpenFont(&FontRequest)))
  2375.     {
  2376.             /* It is probably a diskfont. */
  2377.  
  2378.         if(DiskfontBase = (struct Library *)OpenLibrary("diskfont.library",0))
  2379.         {
  2380.             if(!(DummyFont = (struct TextFont *)OpenDiskFont(&FontRequest)))
  2381.                 return(FALSE);
  2382.             else
  2383.                 CloseLibrary(DiskfontBase);
  2384.         }
  2385.     }
  2386.  
  2387.         /* Attach the font to the RastPort. */
  2388.  
  2389.     SetFont(DummyRPort,DummyFont);
  2390.  
  2391.         /* And set the drawing mode. */
  2392.  
  2393.     SetDrMd(DummyRPort,JAM2);
  2394.  
  2395.     return(TRUE);
  2396. }
  2397.  
  2398.     /* DeleteDummyRPort():
  2399.      *
  2400.      *    Removes the hidden RastPort from memory and get rid
  2401.      *    of the font attached to it.
  2402.      */
  2403.  
  2404. VOID
  2405. DeleteDummyRPort()
  2406. {
  2407.     SHORT i;
  2408.  
  2409.     if(DummyMap)
  2410.     {
  2411.         for(i = 0 ; i < DummyDepth ; i++)
  2412.             if(DummyMap -> Planes[i])
  2413.                 FreeMem(DummyMap -> Planes[i],Byte(Width) * Height);
  2414.  
  2415.         FreeMem(DummyMap,sizeof(struct BitMap));
  2416.  
  2417.         DummyMap = NULL;
  2418.     }
  2419.  
  2420.     if(DummyRPort)
  2421.     {
  2422.         FreeMem(DummyRPort,sizeof(struct RastPort));
  2423.         DummyRPort = NULL;
  2424.     }
  2425.  
  2426.     if(DummyFont)
  2427.     {
  2428.         CloseFont(DummyFont);
  2429.         DummyFont = NULL;
  2430.     }
  2431. }
  2432.  
  2433.     /* MaxFontWidth():
  2434.      *
  2435.      *    Calculate the maximum width of the display by looking for
  2436.      *    the widest letter. Note that currently only fixed width
  2437.      *    fonts are allowed to be used as the system font.
  2438.      */
  2439.  
  2440. UBYTE
  2441. MaxFontWidth()
  2442. {
  2443.     BYTE    FontWidth = 0,TempWidth;
  2444.     char    Shuttle[2];
  2445.     SHORT    i;
  2446.  
  2447.         /* We cannot use a single character to check the width
  2448.          * of a letter, so we'll fake a string.
  2449.          */
  2450.  
  2451.     Shuttle[1] = 0;
  2452.  
  2453.         /* Check all letters. */
  2454.  
  2455.     for(i = 0 ; i < 256 ; i++)
  2456.     {
  2457.         Shuttle[0] = i;
  2458.  
  2459.             /* Is it wider than the last letter? */
  2460.  
  2461.         if((TempWidth = TextLength(Window -> RPort,Shuttle,1)) > FontWidth)
  2462.             FontWidth = TempWidth;
  2463.     }
  2464.  
  2465.     return(FontWidth);
  2466. }
  2467.  
  2468.     /* ShutDown(HandShake):
  2469.      *
  2470.      *    Closes DClock and waits for removal.
  2471.      */
  2472.  
  2473. VOID
  2474. ShutDown(LONG HandShake)
  2475. {
  2476.         /* Restore system functions. */
  2477.  
  2478.     if(OldDisplayBeep)
  2479.         SetFunction((struct Library *)IntuitionBase,-0x60,OldDisplayBeep);
  2480.  
  2481.     if(OldCloseWBench)
  2482.         SetFunction((struct Library *)IntuitionBase,-0x4E,OldCloseWBench);
  2483.  
  2484.     FlushHandler();
  2485.     FlushSound();
  2486.  
  2487.     if(Window)
  2488.     {
  2489.         SetRast(DummyRPort,1);
  2490.  
  2491.         ClipBlit(DummyRPort,0,0,Window -> WScreen -> BarLayer -> rp,LeftEdge,1,Width,Height,0xC0);
  2492.  
  2493.         CloseWindow(Window);
  2494.     }
  2495.  
  2496.     DeleteDummyRPort();
  2497.  
  2498.         /* Return the special signals. */
  2499.  
  2500.     if(BenchSig != -1)
  2501.         FreeSignal(BenchSig);
  2502.  
  2503.     if(DisplaySig != 1)
  2504.         FreeSignal(DisplaySig);
  2505.  
  2506.     if(SpeechSig != 1)
  2507.         FreeSignal(SpeechSig);
  2508.  
  2509.         /* Free the Rexx host port. */
  2510.  
  2511.     if(DSeg -> RexxHost)
  2512.         DeleteRexxHost(DSeg -> RexxHost);
  2513.  
  2514.     if(RexxProcess)
  2515.     {
  2516.         Signal(RexxProcess,SIG_CLOSE);
  2517.  
  2518.         Wait(SIG_SHAKE);
  2519.     }
  2520.  
  2521.     if(SpeechProcess)
  2522.     {
  2523.         Signal(SpeechProcess,SIG_CLOSE);
  2524.  
  2525.         Wait(SIG_SHAKE);
  2526.     }
  2527.  
  2528.     if(ArpBase)
  2529.         CloseLibrary(ArpBase);
  2530.  
  2531.         /* Sneak out before someone can
  2532.          * UnLoadSeg() us.
  2533.          */
  2534.  
  2535.     Forbid();
  2536.  
  2537.         /* Goodbye father. */
  2538.  
  2539.     if(DSeg -> Father)
  2540.         Signal(DSeg -> Father,HandShake);
  2541. }
  2542.  
  2543.     /* _main():
  2544.      *
  2545.      *    Modified Aztec C startup-routine, no CLI parsing,
  2546.      *    no Workbench parsing, absolutely nothing.
  2547.      */
  2548.  
  2549. LONG
  2550. _main()
  2551. {
  2552.     volatile ULONG         MemSize,MaxSize;
  2553.  
  2554.     ULONG             SignalSet;
  2555.     UBYTE             Force,Blink = FALSE,i,EmptyWidth,FullWidth;
  2556.  
  2557.     LONG             RexxMask = 0;
  2558.  
  2559.     struct Screen        *Workbench;
  2560.  
  2561.     HandlerProcess = (struct Process *)SysBase -> ThisTask;
  2562.  
  2563.         /* If somebody called us from CLI... */
  2564.  
  2565.     if(HandlerProcess -> pr_CLI)
  2566.         return(-1);
  2567.  
  2568.         /* Is the DSeg structure anywhere? */
  2569.  
  2570.     DSeg = (struct DSeg *)FindPort(PORTNAME);
  2571.  
  2572.         /* Sneak out if handler was accidentally started from
  2573.          * Workbench.
  2574.          */
  2575.  
  2576.     if(!DSeg || !DSeg -> Father)
  2577.     {
  2578.         struct WBStartup *WBenchMsg;
  2579.  
  2580.         WaitPort(&HandlerProcess -> pr_MsgPort);
  2581.  
  2582.         WBenchMsg = (struct WBStartup *)GetMsg(&HandlerProcess -> pr_MsgPort);
  2583.  
  2584.         if(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",0))
  2585.         {
  2586.             DisplayBeep(NULL);
  2587.             CloseLibrary(IntuitionBase);
  2588.         }
  2589.  
  2590.         Forbid();
  2591.  
  2592.         ReplyMsg(WBenchMsg);
  2593.  
  2594.         return;
  2595.     }
  2596.  
  2597.         /* Check if it's below our current revision
  2598.          * number (probably doesn't support some
  2599.          * structure tags).
  2600.          */
  2601.  
  2602.     if(DSeg -> Revision < REVISION)
  2603.     {
  2604.         Forbid();
  2605.         Signal(DSeg -> Father,DSeg -> RingBack);
  2606.  
  2607.         return;
  2608.     }
  2609.  
  2610.     if(!(ArpBase = (struct ArpBase *)OpenLibrary(ArpName,ArpVersion)))
  2611.     {
  2612.         Forbid();
  2613.         Signal(DSeg -> Father,DSeg -> RingBack);
  2614.  
  2615.         return;
  2616.     }
  2617.  
  2618.     IntuitionBase    = ArpBase -> IntuiBase;
  2619.     GfxBase        = ArpBase -> GfxBase;
  2620.  
  2621.         /* Open RexxHostLibrary if possible. If the open
  2622.          * fails, it doesn't matter.
  2623.          */
  2624.  
  2625.     RexxHostBase = ArpOpenLibrary("rexxhost.library",34);
  2626.  
  2627.         /* Try to find the Workbench screen. */
  2628.  
  2629.     if(!(Workbench = (struct Screen *)FindTheBench()))
  2630.     {
  2631.         ShutDown(DSeg -> RingBack);
  2632.  
  2633.         return;
  2634.     }
  2635.  
  2636.         /* Open the backdrop window. */
  2637.  
  2638.     if(!(Window = (struct Window *)OpenWindow(&NewWindow)))
  2639.     {
  2640.         ShutDown(DSeg -> RingBack);
  2641.  
  2642.         return;
  2643.     }
  2644.  
  2645.         /* Initialize the click sound data. */
  2646.  
  2647.     if(!InitSound())
  2648.     {
  2649.         ShutDown(DSeg -> RingBack);
  2650.  
  2651.         return;
  2652.     }
  2653.  
  2654.         /* Install the handler code. */
  2655.  
  2656.     if(!InitHandler())
  2657.     {
  2658.         ShutDown(DSeg -> RingBack);
  2659.  
  2660.         return;
  2661.     }
  2662.  
  2663.     if((BenchSig = AllocSignal(-1)) == -1)
  2664.     {
  2665.         ShutDown(DSeg -> RingBack);
  2666.  
  2667.         return;
  2668.     }
  2669.  
  2670.     if((DisplaySig = AllocSignal(-1)) == -1)
  2671.     {
  2672.         ShutDown(DSeg -> RingBack);
  2673.  
  2674.         return;
  2675.     }
  2676.  
  2677.     if((SpeechSig = AllocSignal(-1)) == -1)
  2678.     {
  2679.         ShutDown(DSeg -> RingBack);
  2680.  
  2681.         return;
  2682.     }
  2683.  
  2684.         /* Adjust the window left offset. */
  2685.  
  2686.     Width        = MaxFontWidth() * 22;
  2687.     Height        = Window -> IFont -> tf_YSize;
  2688.  
  2689.     LeftEdge    = Workbench -> Width - 55 - Width;
  2690.  
  2691.     EmptyWidth    = TextLength(Window -> RPort,"E",1);
  2692.     FullWidth    = TextLength(Window -> RPort,"F",1);
  2693.  
  2694.         /* Create the dummy RastPort. */
  2695.  
  2696.     if(!CreateDummyRPort())
  2697.     {
  2698.         ShutDown(DSeg -> RingBack);
  2699.  
  2700.         return;
  2701.     }
  2702.  
  2703.         /* Check if we are running unter Kickstart 2.x. */
  2704.  
  2705.     if(SysBase -> LibNode . lib_Version >= 36)
  2706.     {
  2707.         USHORT TempCol;
  2708.  
  2709.         NewKick = TRUE;
  2710.  
  2711.         for(i = 0 ; i < 228 ; i++)
  2712.         {
  2713.             TempCol = ClockMap[i];
  2714.             ClockMap[i] = ClockMap[i + 228];
  2715.             ClockMap[i + 228] = TempCol;
  2716.         }
  2717.  
  2718.         for(i = 0 ; i < 3 ; i++)
  2719.             ClockTxt[i] . FrontPen = 1;
  2720.  
  2721.         LeftEdge = Workbench -> Width - 25 - Width;
  2722.     }
  2723.  
  2724.         /* Create the rexx server task. */
  2725.  
  2726.     if(RexxHostBase)
  2727.     {
  2728.         if(!(RexxProcess = (struct Process *)CreateFuncProc("DClock-Rexx",5,RexxServer,4000)))
  2729.         {
  2730. Shut:            ShutDown(DSeg -> RingBack);
  2731.  
  2732.             return;
  2733.         }
  2734.  
  2735.             /* Wait for handshake. */
  2736.  
  2737.         Wait(SIG_SHAKE);
  2738.  
  2739.             /* No port? Fail fast! */
  2740.  
  2741.         if(!RexxTaskPort)
  2742.             goto Shut;
  2743.     }
  2744.  
  2745.         /* Fill in the window and bring it to the front. */
  2746.  
  2747.     ShowTime(FALSE,TRUE);
  2748.  
  2749.         /* Patch system function. */
  2750.  
  2751.     OldDisplayBeep = SetFunction((struct Library *)IntuitionBase,-0x60,NewDisplayBeep);
  2752.     OldCloseWBench = SetFunction((struct Library *)IntuitionBase,-0x4E,NewCloseWBench);
  2753.  
  2754.         /* If RexxHostBase is around, add the rexx port. */
  2755.  
  2756.     if(RexxHostBase)
  2757.     {
  2758.         if(DSeg -> RexxHost = CreateRexxHost("DCLOCK"))
  2759.             RexxMask = (1 << DSeg -> RexxHost -> rh_Port . mp_SigBit);
  2760.     }
  2761.  
  2762.         /* Initialize the signal-semaphore. */
  2763.  
  2764.     InitSemaphore(DSeg -> SoundSemaphore);
  2765.  
  2766.         /* Now we are truly running. */
  2767.  
  2768.     DSeg -> Child = SysBase -> ThisTask;
  2769.  
  2770.         /* How much memory is there around in this
  2771.          * Amiga?
  2772.          */
  2773.  
  2774.     MaxSize = MaxMemSize(MEMF_CHIP) + MaxMemSize(MEMF_FAST);
  2775.  
  2776.         /* Tell father to finish. */
  2777.  
  2778.     Signal(DSeg -> Father,DSeg -> RingBack);
  2779.  
  2780.     DSeg -> Father = NULL;
  2781.  
  2782.         /* Go into infinite loop waiting for signals. */
  2783.  
  2784.     FOREVER
  2785.     {
  2786.         SignalSet = Wait(SIG_TIMER | SIG_CLOSE | SIG_CLICK | SIG_TOGGL | SIG_BENCH | SIG_WINDO | SIG_DISPL | SIG_SPEECH | RexxMask);
  2787.  
  2788.             /* Was it a Rexx call? */
  2789.  
  2790.         if((SignalSet & RexxMask) && RexxMask)
  2791.         {
  2792.             struct RexxMessage *RexxMsg;
  2793.  
  2794.                 /* Capture the messages... */
  2795.  
  2796.             while(RexxMsg = GetMsg((struct MsgPort *)DSeg -> RexxHost))
  2797.             {
  2798.                     /* Send them to the rexx server task. */
  2799.  
  2800.                 if(GetRexxCommand(RexxMsg))
  2801.                     PutMsg(RexxTaskPort,RexxMsg);
  2802.                 else
  2803.                     FreeRexxCommand(RexxMsg);
  2804.             }
  2805.         }
  2806.  
  2807.             /* Change the display mode? */
  2808.  
  2809.         if(SignalSet & SIG_TOGGL)
  2810.         {
  2811.             DSeg -> Page++;
  2812.  
  2813.             SetRast(DummyRPort,1);
  2814.  
  2815.             if(DSeg -> Page == 5)
  2816.                 DSeg -> Page = 0;
  2817.  
  2818.             if(DSeg -> Countdown < 1 && DSeg -> Page == 4)
  2819.                 DSeg -> Page = 0;
  2820.  
  2821.             Force = TRUE;
  2822.         }
  2823.  
  2824.             /* Are we to click? */
  2825.  
  2826.         if((SignalSet & SIG_CLICK) && DSeg -> Click)
  2827.             Click();
  2828.  
  2829.             /* Are we to shut down? */
  2830.  
  2831.         if(SignalSet & SIG_CLOSE)
  2832.         {
  2833.             if(ChimeAudioBlock)
  2834.                 StopChime();
  2835.  
  2836.             ShutDown(SIG_CLOSE);
  2837.             return;
  2838.         }
  2839.  
  2840.         if(SignalSet & SIG_BENCH)
  2841.         {
  2842.                 /* Close the window... */
  2843.  
  2844.             if(Window)
  2845.             {
  2846.                 CloseWindow(Window);
  2847.                 Window = NULL;
  2848.  
  2849.                 DeleteDummyRPort();
  2850.  
  2851.                 Delay(25);
  2852.  
  2853.                 FOREVER
  2854.                 {
  2855.                         /* Wait for wakeup call... */
  2856.  
  2857.                     if((SetSignal(NULL,NULL) & SIG_CLOSE) == SIG_CLOSE)
  2858.                     {
  2859.                         SetSignal(NULL,SIG_CLOSE);
  2860.  
  2861.                         SignalSet = SIG_CLOSE;
  2862.                         break;
  2863.                     }
  2864.  
  2865.                     if(Workbench = (struct Screen *)FindTheBench())
  2866.                     {
  2867.                         SignalSet = NULL;
  2868.                         break;
  2869.                     }
  2870.  
  2871.                     Delay(25);
  2872.                 }
  2873.             }
  2874.  
  2875.                 /* Finish? */
  2876.  
  2877.             if(SignalSet & SIG_CLOSE)
  2878.             {
  2879.                 if(ChimeAudioBlock)
  2880.                     StopChime();
  2881.  
  2882.                 ShutDown(SIG_CLOSE);
  2883.                 return;
  2884.             }
  2885.  
  2886.                 /* Open the window. */
  2887.  
  2888.             if(!(Window = (struct Window *)OpenWindow(&NewWindow)))
  2889.             {
  2890. BeatIt:                if(ChimeAudioBlock)
  2891.                     StopChime();
  2892.  
  2893.                 ShutDown(SIG_CLOSE);
  2894.                 return;
  2895.             }
  2896.  
  2897.             Width    = MaxFontWidth() * 22;
  2898.             Height    = Window -> IFont -> tf_YSize;
  2899.  
  2900.                 /* Re-adjust the Window offset. */
  2901.  
  2902.             if(NewKick)
  2903.                 LeftEdge = Workbench -> Width - 25 - Width;
  2904.             else
  2905.                 LeftEdge = Workbench -> Width - 55 - Width;
  2906.  
  2907.             EmptyWidth    = TextLength(Window -> RPort,"E",1);
  2908.             FullWidth    = TextLength(Window -> RPort,"F",1);
  2909.  
  2910.                 /* Redraw the time if necessary. */
  2911.  
  2912.             Printed = FALSE;
  2913.  
  2914.             if(!CreateDummyRPort())
  2915.                 goto BeatIt;
  2916.         }
  2917.  
  2918.             /* A window refresh call came in. */
  2919.  
  2920.         if(SignalSet & SIG_WINDO)
  2921.         {
  2922.             struct IntuiMessage *IMsg;
  2923.  
  2924.                 /* Reply the Message (don't need it). */
  2925.  
  2926.             if(IMsg = GetMsg(Window -> UserPort))
  2927.                 ReplyMsg(IMsg);
  2928.         }
  2929.  
  2930.             /* Give it a brief beep to indicate the hour? */
  2931.  
  2932.         if(DSeg -> Hour && !DSeg -> CurrentTime . Minute && !DSeg -> CurrentTime . Second)
  2933.             PlayChime();
  2934.  
  2935.             /* Check if chime has ended. */
  2936.  
  2937.         if(ChimeAudioBlock)
  2938.         {
  2939.             if(CheckIO(ChimeAudioBlock))
  2940.                 StopChime();
  2941.         }
  2942.  
  2943.             /* Ready to ring the bell? */
  2944.  
  2945.         if(DSeg -> CurrentTime . Hour   == DSeg -> AlarmHour &&
  2946.            DSeg -> CurrentTime . Minute == DSeg -> AlarmMinute &&
  2947.            DSeg -> CurrentTime . Second == DSeg -> AlarmSecond &&
  2948.            DSeg -> Alarm)
  2949.             Ring(TRUE);
  2950.  
  2951.             /* If needed, decrement the tea timer. */
  2952.  
  2953.         if(DSeg -> Countdown > 0)
  2954.         {
  2955.             if(!(--DSeg -> Countdown))
  2956.                 Ring(FALSE);
  2957.         }
  2958.  
  2959.             /* Check the display signal before we
  2960.              * redraw it.
  2961.              */
  2962.  
  2963.         if(SignalSet & SIG_DISPL)
  2964.         {
  2965.             DSeg -> Seconds ^= TRUE;
  2966.  
  2967.             Force = TRUE;
  2968.         }
  2969.  
  2970.             /* We are to tell the time. */
  2971.  
  2972.         if(SignalSet & SIG_SPEECH)
  2973.         {
  2974.                 /* Remove speech process if speech
  2975.                  * has been turned off after it has
  2976.                  * been run once.
  2977.                  */
  2978.  
  2979.             if(!DSeg -> Speech)
  2980.             {
  2981.                 if(SpeechProcess)
  2982.                 {
  2983.                     Signal(SpeechProcess,SIG_CLOSE);
  2984.  
  2985.                     Wait(SIG_SHAKE);
  2986.                 }
  2987.             }
  2988.             else
  2989.             {
  2990.                     /* Try to create the speech process. */
  2991.  
  2992.                 if(!SpeechProcess)
  2993.                 {
  2994.                     Forbid();
  2995.  
  2996.                     if(SpeechProcess = CreateFuncProc("DClock-Speech",5,SpeechServer,4000))
  2997.                         Wait(SIG_SHAKE);
  2998.  
  2999.                     Permit();
  3000.                 }
  3001.  
  3002.                     /* Tell the time. */
  3003.  
  3004.                 if(SpeechProcess)
  3005.                     Signal(SpeechProcess,SIG_CLICK);
  3006.             }
  3007.         }
  3008.  
  3009.             /* Show time and date. */
  3010.  
  3011.         if(DSeg -> Page == 0)
  3012.             ShowTime(TRUE,Force);
  3013.         else
  3014.             ShowTime(FALSE,Force);
  3015.  
  3016.         Force = FALSE;
  3017.  
  3018.             /* Show memory display. */
  3019.  
  3020.         if(DSeg -> Page == 1)
  3021.         {
  3022.             LONG BarLength;
  3023.  
  3024.                 /* For single bitplane Workbench
  3025.                  * screen -> use fill pattern.
  3026.                  */
  3027.  
  3028.             static USHORT Checkers[8]=
  3029.             {
  3030.                 0xAAAA,0x5555,
  3031.                 0xAAAA,0x5555,
  3032.                 0xAAAA,0x5555,
  3033.                 0xAAAA,0x5555
  3034.             };
  3035.  
  3036.                 /* How much memory is still available? */
  3037.  
  3038.             MemSize = AvailMem(MEMF_CHIP) + AvailMem(MEMF_FAST);
  3039.  
  3040.                 /* How LONG will the bar be? */
  3041.  
  3042.             BarLength = ((Width - (EmptyWidth + FullWidth)) * MemSize) / MaxSize;
  3043.  
  3044.             if(NewKick)
  3045.             {
  3046.                 SetAPen(DummyRPort,2);
  3047.                 SetBPen(DummyRPort,3);
  3048.             }
  3049.             else
  3050.             {
  3051.                 SetAPen(DummyRPort,1);
  3052.                 SetBPen(DummyRPort,2);
  3053.             }
  3054.  
  3055.             Move(DummyRPort,0,Window -> IFont -> tf_Baseline);
  3056.  
  3057.             Text(DummyRPort,"E",1);
  3058.  
  3059.             Move(DummyRPort,Width - FullWidth,Window -> IFont -> tf_Baseline);
  3060.  
  3061.             Text(DummyRPort,"F",1);
  3062.  
  3063.                 /* Draw the full part. */
  3064.  
  3065.             if(Window -> RPort -> BitMap -> Depth < 2)
  3066.                 SetAfPt(DummyRPort,&Checkers[0],1);
  3067.  
  3068.             if(NewKick)
  3069.                 SetAPen(DummyRPort,2);
  3070.             else
  3071.                 SetAPen(DummyRPort,3);
  3072.  
  3073.             RectFill(DummyRPort,EmptyWidth,0,Width - FullWidth - BarLength,Height - 1);
  3074.  
  3075.             if(Window -> RPort -> BitMap -> Depth < 2)
  3076.                 SetAfPt(DummyRPort,NULL,0);
  3077.  
  3078.                 /* Add the empty part. */
  3079.  
  3080.             if(NewKick)
  3081.                 SetAPen(DummyRPort,3);
  3082.             else
  3083.                 SetAPen(DummyRPort,2);
  3084.  
  3085.             RectFill(DummyRPort,Width - FullWidth - BarLength,0,Width - (FullWidth + 1),Height - 1);
  3086.         }
  3087.  
  3088.             /* Numeric memory display. */
  3089.  
  3090.         if(DSeg -> Page == 2)
  3091.         {
  3092.             UBYTE TempBuff[30];
  3093.  
  3094.             SPrintf(TempBuff,"C: %07ld  F: %07ld",AvailMem(MEMF_CHIP),AvailMem(MEMF_FAST));
  3095.             PrintIt(TempBuff);
  3096.         }
  3097.  
  3098.             /* Show online timer? */
  3099.  
  3100.         if(DSeg -> Page == 3)
  3101.         {
  3102.             UBYTE TempBuff[50];
  3103.  
  3104.             SPrintf(TempBuff,"OFFLINE %02ld:%02ld:%02ld",OnlineHours,OnlineMinutes,OnlineSeconds);
  3105.  
  3106.             if(DSeg -> Online)
  3107.             {
  3108.                 if(Blink ^= TRUE)
  3109.                 {
  3110.                     TempBuff[0] = ' ';
  3111.                     TempBuff[1] = 'O';
  3112.                     TempBuff[2] = 'N';
  3113.                 }
  3114.                 else
  3115.                 {
  3116.                     TempBuff[0] = ' ';
  3117.                     TempBuff[1] = ' ';
  3118.                     TempBuff[2] = ' ';
  3119.                 }
  3120.             }
  3121.  
  3122.             PrintIt(TempBuff);
  3123.         }
  3124.  
  3125.             /* Show the countdown. */
  3126.  
  3127.         if(DSeg -> Page == 4)
  3128.         {
  3129.             UBYTE TempBuff[50];
  3130.  
  3131.             if(DSeg -> Countdown > 0)
  3132.             {
  3133.                 SPrintf(TempBuff,"Countdown %ld         ",-DSeg -> Countdown);
  3134.                 TempBuff[22] = 0;
  3135.  
  3136.                 PrintIt(TempBuff);
  3137.             }
  3138.             else
  3139.                 DSeg -> Page = 0;
  3140.         }
  3141.  
  3142.             /* Transfer the image portion. */
  3143.  
  3144.         ClipBlit(DummyRPort,0,0,Window -> WScreen -> BarLayer -> rp,LeftEdge,1,Width,Height,0xC0);
  3145.     }
  3146. }
  3147.